You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit fc7e7f7
Browse filesBrowse the repository at this point in the historyBrowse files
feat(metadata-protocol): enforce package namespace prefix for Studio-authored packages (#2694)
* feat(metadata-protocol): enforce package namespace prefix for Studio-authored packages
The protocol requires every object.name to carry its package's manifest.namespace
prefix (crm_account); defineStack enforces this at compile time. Studio/runtime-
authored packages never took that path and were created without a namespace at
all, so the rule was silently inert and objects published with bare, collision-
prone names (e.g. two packages could each publish `leave_request`).
- installPackage: derive a default namespace from the package id when the
manifest declares none (com.example.leave → leave) and persist it on the
manifest; an explicit namespace always wins (HotCRM's crm is untouched).
- publishPackageDrafts: reject any object draft missing the <ns>_ prefix before
promoting anything (atomic), with an actionable "rename to ..." message.
Namespace-less packages are grandfathered, mirroring defineStack.
The per-object check and id→namespace derivation are extracted to
@objectstack/spec/kernel as the single source shared by defineStack and the
runtime publish path, so the two enforcement points cannot drift.
Found by dogfooding package-first authoring: a Studio-built package
(com.example.leave) persisted namespace=null, so its object leave_request had no
prefix and published with zero validation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(spec): regenerate api-surface for new namespace-prefix exports
Additive-only: registers validateObjectNamespacePrefix and
deriveNamespaceFromPackageId (both new exports from @objectstack/spec/kernel)
in the frozen API-surface snapshot. Consistent with the minor bump already in
the changeset.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* - `MetadataManager.publishPackage()` (runtime, Studio "全部发布").
15
+
*/
16
+
17
+
/** Namespace charset accepted by `manifest.namespace` (2-20 chars). */
18
+
constNAMESPACE_RE=/^[a-z][a-z0-9_]{1,19}$/;
19
+
20
+
/**
21
+
* Validate a single object name against a package namespace prefix.
22
+
*
23
+
* Returns an actionable error message, or `null` when the name is compliant.
24
+
* `sys_*` names are platform-reserved and always allowed. When `namespace` is
25
+
* empty the check is skipped (returns `null`) — callers decide whether an
26
+
* absent namespace is itself an error.
27
+
*/
28
+
exportfunctionvalidateObjectNamespacePrefix(
29
+
objectName: string|undefined,
30
+
namespace: string|undefined,
31
+
): string|null{
32
+
if(!objectName||!namespace)returnnull;
33
+
if(objectName.startsWith('sys_'))returnnull;
34
+
35
+
constexpectedPrefix=`${namespace}_`;
36
+
if(objectName.includes('__')){
37
+
return`Object '${objectName}' uses the legacy FQN form '<ns>__<short>'. Rename it to '${expectedPrefix}${objectName.slice(objectName.indexOf('__')+2)}'.`;
38
+
}
39
+
if(!objectName.startsWith(expectedPrefix)){
40
+
return`Object '${objectName}' is missing the package namespace prefix. Rename it to '${expectedPrefix}${objectName}' (namespace = '${namespace}').`;
41
+
}
42
+
returnnull;
43
+
}
44
+
45
+
/**
46
+
* Derive a default namespace from a package id when the manifest declares none.
47
+
*
48
+
* Uses the last dot-segment of the id (`com.example.leave` → `leave`),
49
+
* lowercased and sanitized to the namespace charset. Returns `null` when
50
+
* nothing valid can be derived (caller then leaves the namespace unset rather
51
+
* than inventing a bad one). This only supplies a DEFAULT for packages that
52
+
* omit `namespace`; an explicitly declared namespace always wins.
0 commit comments