@jini-ai/cms 0.3.11 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/README.md +47 -19
- package/dist/content-types/agent-tools.d.ts +3 -56
- package/dist/content-types/agent-tools.js +22 -6
- package/dist/content-types/cleanup.d.ts +18 -13
- package/dist/content-types/cleanup.js +16 -11
- package/dist/content-types/errors.d.ts +57 -24
- package/dist/content-types/errors.js +56 -39
- package/dist/content-types/field-defs.d.ts +8 -4
- package/dist/content-types/field-defs.js +19 -14
- package/dist/content-types/index-provisioning.d.ts +61 -47
- package/dist/content-types/index-provisioning.js +90 -66
- package/dist/content-types/index.d.ts +4 -3
- package/dist/content-types/lifecycle-dispatch.d.ts +11 -5
- package/dist/content-types/lifecycle-dispatch.js +5 -4
- package/dist/content-types/lifecycle.d.ts +23 -18
- package/dist/content-types/lifecycle.js +71 -64
- package/dist/content-types/repo.memory.d.ts +5 -4
- package/dist/content-types/repo.memory.js +3 -2
- package/dist/content-types/tool-registrations.d.ts +6 -8
- package/dist/content-types/tool-registrations.js +24 -28
- package/dist/content-types/types.d.ts +17 -13
- package/dist/content-types/types.js +11 -5
- package/dist/content-types/write-service.d.ts +20 -22
- package/dist/content-types/write-service.js +78 -75
- package/dist/core/authorization.d.ts +15 -0
- package/dist/core/authorization.js +2 -0
- package/dist/core/commands/change-set.d.ts +15 -7
- package/dist/core/commands/command.d.ts +45 -26
- package/dist/core/commands/command.js +73 -63
- package/dist/core/entity-liveness.d.ts +11 -7
- package/dist/core/entity-liveness.js +5 -4
- package/dist/core/index.d.ts +12 -5
- package/dist/core/index.js +10 -4
- package/dist/core/ports.d.ts +25 -24
- package/dist/core/ports.js +1 -0
- package/dist/core/tools/args.d.ts +49 -0
- package/dist/core/tools/args.js +46 -0
- package/dist/core/tools/index.d.ts +3 -0
- package/dist/core/tools/index.js +3 -0
- package/dist/entries/agent-tools.d.ts +1 -73
- package/dist/entries/agent-tools.js +19 -13
- package/dist/entries/errors.d.ts +28 -14
- package/dist/entries/errors.js +17 -11
- package/dist/entries/field-validation.d.ts +5 -3
- package/dist/entries/field-validation.js +7 -5
- package/dist/entries/index.d.ts +2 -2
- package/dist/entries/lifecycle-dispatch.d.ts +5 -2
- package/dist/entries/lifecycle-dispatch.js +2 -1
- package/dist/entries/list.d.ts +4 -2
- package/dist/entries/list.js +4 -3
- package/dist/entries/repo.memory.d.ts +12 -10
- package/dist/entries/repo.memory.js +11 -10
- package/dist/entries/tool-registrations.d.ts +6 -8
- package/dist/entries/tool-registrations.js +42 -41
- package/dist/entries/types.d.ts +0 -7
- package/dist/entries/write-service.d.ts +28 -29
- package/dist/entries/write-service.js +114 -105
- package/dist/http/settings/cms-adapter.d.ts +20 -0
- package/dist/http/settings/cms-adapter.js +124 -0
- package/dist/http/settings/contracts.d.ts +135 -0
- package/dist/http/settings/contracts.js +2 -0
- package/dist/http/settings/http.d.ts +10 -0
- package/dist/http/settings/http.js +211 -0
- package/dist/http/settings/index.d.ts +5 -0
- package/dist/http/settings/index.js +4 -0
- package/dist/http/settings/shared.d.ts +68 -0
- package/dist/http/settings/shared.js +54 -0
- package/dist/http/settings/stream.d.ts +24 -0
- package/dist/http/settings/stream.js +147 -0
- package/dist/media/agent-tools.d.ts +1 -66
- package/dist/media/agent-tools.js +5 -1
- package/dist/media/blob-gc-lock.d.ts +6 -2
- package/dist/media/blob-gc-lock.js +4 -2
- package/dist/media/blob-gc.d.ts +64 -51
- package/dist/media/blob-gc.js +62 -52
- package/dist/media/blob-key.d.ts +1 -1
- package/dist/media/content-type-sniffer.d.ts +3 -1
- package/dist/media/content-type-sniffer.js +1 -1
- package/dist/media/html-attributes.d.ts +9 -5
- package/dist/media/html-attributes.js +8 -6
- package/dist/media/image-transformer.js +1 -1
- package/dist/media/image-transformer.sharp.d.ts +6 -0
- package/dist/media/image-transformer.sharp.js +12 -6
- package/dist/media/import/fetch-image.d.ts +24 -0
- package/dist/media/import/fetch-image.js +137 -0
- package/dist/media/import/index.d.ts +3 -0
- package/dist/media/import/index.js +3 -0
- package/dist/media/import/ports.d.ts +38 -0
- package/dist/media/index.d.ts +7 -1
- package/dist/media/index.js +1 -0
- package/dist/media/media-service.d.ts +14 -10
- package/dist/media/media-service.js +57 -52
- package/dist/media/ports.d.ts +3 -2
- package/dist/media/rendition-service.d.ts +3 -3
- package/dist/media/rendition-service.js +38 -37
- package/dist/media/repo.memory.d.ts +18 -7
- package/dist/media/repo.memory.js +12 -6
- package/dist/media/tool-registrations.d.ts +16 -15
- package/dist/media/tool-registrations.js +84 -72
- package/dist/media/transform-lock.d.ts +22 -12
- package/dist/media/transform-lock.js +14 -13
- package/dist/media/transform-registry.d.ts +3 -3
- package/dist/media/transform-registry.js +19 -18
- package/dist/media/transform-types.d.ts +8 -3
- package/dist/media/transform-types.js +10 -5
- package/dist/media/types.d.ts +21 -4
- package/dist/media/types.js +15 -2
- package/dist/navigation/agent-tools.d.ts +1 -80
- package/dist/navigation/agent-tools.js +15 -3
- package/dist/navigation/contracts.d.ts +1 -1
- package/dist/navigation/index.d.ts +2 -2
- package/dist/navigation/menu-service.d.ts +57 -40
- package/dist/navigation/menu-service.js +80 -69
- package/dist/navigation/ports.d.ts +1 -1
- package/dist/navigation/read-model.d.ts +3 -1
- package/dist/navigation/read-model.js +2 -2
- package/dist/navigation/reconcile.d.ts +2 -2
- package/dist/navigation/reconcile.js +2 -1
- package/dist/navigation/repo.memory.d.ts +7 -3
- package/dist/navigation/repo.memory.js +4 -2
- package/dist/navigation/resolver.d.ts +4 -1
- package/dist/navigation/resolver.js +1 -1
- package/dist/navigation/tool-registrations.d.ts +6 -8
- package/dist/navigation/tool-registrations.js +44 -42
- package/dist/navigation/types.d.ts +1 -1
- package/dist/presentation/presentation.d.ts +11 -3
- package/dist/presentation/presentation.js +13 -6
- package/dist/presentation/repo.memory.d.ts +6 -2
- package/dist/presentation/repo.memory.js +3 -2
- package/dist/settings/agent-tools.d.ts +2 -75
- package/dist/settings/agent-tools.js +49 -45
- package/dist/settings/agent-value-write-tools.d.ts +22 -0
- package/dist/settings/agent-value-write-tools.js +154 -0
- package/dist/settings/agent-writable-preferences.js +8 -20
- package/dist/settings/agent-write-denylist.d.ts +19 -0
- package/dist/settings/agent-write-denylist.js +15 -0
- package/dist/settings/change-feed.d.ts +11 -2
- package/dist/settings/change-feed.js +4 -4
- package/dist/settings/definitions-dispatch.d.ts +8 -3
- package/dist/settings/definitions-dispatch.js +5 -5
- package/dist/settings/dictionaries/index.d.ts +5 -2
- package/dist/settings/dictionaries/index.js +2 -2
- package/dist/settings/dictionaries/settings-dialog.ar.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.bn.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.de.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.es.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.fa.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.fr.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.hi.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.hu.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.id.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.it.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.ja.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.ko.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.pl.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.pt-BR.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.ru.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.th.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.tr.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.uk.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.ur.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.zh-CN.js +0 -1
- package/dist/settings/dictionaries/settings-dialog.zh-TW.js +0 -1
- package/dist/settings/ensure-definitions.d.ts +5 -5
- package/dist/settings/errors.d.ts +7 -1
- package/dist/settings/errors.js +7 -1
- package/dist/settings/index.d.ts +4 -1
- package/dist/settings/index.js +1 -0
- package/dist/settings/ports.d.ts +6 -5
- package/dist/settings/principal-lookup.d.ts +9 -0
- package/dist/settings/principal-lookup.js +2 -0
- package/dist/settings/purge-service.d.ts +5 -4
- package/dist/settings/purge-service.js +3 -2
- package/dist/settings/settings.d.ts +22 -7
- package/dist/settings/settings.js +47 -26
- package/dist/settings/tool-registrations.d.ts +25 -32
- package/dist/settings/tool-registrations.js +66 -56
- package/dist/settings/types.d.ts +1 -1
- package/dist/settings/ui-tab-definitions.d.ts +1 -1
- package/dist/settings/write-service.d.ts +32 -26
- package/dist/settings/write-service.js +26 -20
- package/dist/taxonomy/agent-tools.d.ts +1 -66
- package/dist/taxonomy/agent-tools.js +25 -15
- package/dist/taxonomy/content-lookup.js +1 -1
- package/dist/taxonomy/index.d.ts +2 -2
- package/dist/taxonomy/merge-term.d.ts +7 -4
- package/dist/taxonomy/merge-term.js +7 -5
- package/dist/taxonomy/repo.memory.d.ts +42 -19
- package/dist/taxonomy/repo.memory.js +14 -12
- package/dist/taxonomy/validation-chain.d.ts +39 -18
- package/dist/taxonomy/validation-chain.js +35 -25
- package/dist/taxonomy/write-service.d.ts +103 -44
- package/dist/taxonomy/write-service.js +221 -179
- package/dist/trash/adapters/record-store.d.ts +52 -0
- package/dist/trash/adapters/record-store.js +36 -0
- package/dist/trash/cursor.d.ts +20 -0
- package/dist/trash/cursor.js +36 -0
- package/dist/trash/follow-ups.d.ts +76 -0
- package/dist/trash/follow-ups.js +46 -0
- package/dist/trash/index.d.ts +8 -0
- package/dist/trash/index.js +8 -0
- package/dist/trash/ports.d.ts +308 -0
- package/dist/trash/ports.js +2 -0
- package/dist/trash/repo.memory.d.ts +50 -0
- package/dist/trash/repo.memory.js +114 -0
- package/dist/trash/sweeper.d.ts +77 -0
- package/dist/trash/sweeper.js +127 -0
- package/dist/trash/write-service.d.ts +100 -0
- package/dist/trash/write-service.js +242 -0
- package/dist/workspace/agent-tools.d.ts +1 -59
- package/dist/workspace/agent-tools.js +27 -12
- package/dist/workspace/create.d.ts +31 -6
- package/dist/workspace/create.js +18 -5
- package/dist/workspace/delete.d.ts +18 -13
- package/dist/workspace/delete.js +20 -12
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/repo.memory.d.ts +19 -4
- package/dist/workspace/repo.memory.js +26 -4
- package/dist/workspace/tool-registrations.d.ts +2 -2
- package/dist/workspace/tool-registrations.js +13 -13
- package/dist/workspace/update.d.ts +6 -5
- package/dist/workspace/update.js +9 -8
- package/package.json +44 -38
- package/dist/identity/admin-crud-service.d.ts +0 -280
- package/dist/identity/admin-crud-service.js +0 -468
- package/dist/identity/agent-tool-input.d.ts +0 -58
- package/dist/identity/agent-tool-input.js +0 -126
- package/dist/identity/agent-tools.d.ts +0 -101
- package/dist/identity/agent-tools.js +0 -290
- package/dist/identity/auth-service.d.ts +0 -118
- package/dist/identity/auth-service.js +0 -170
- package/dist/identity/authorize.d.ts +0 -90
- package/dist/identity/authorize.js +0 -94
- package/dist/identity/grant-service.d.ts +0 -198
- package/dist/identity/grant-service.js +0 -361
- package/dist/identity/hasher.d.ts +0 -39
- package/dist/identity/hasher.js +0 -39
- package/dist/identity/index.d.ts +0 -62
- package/dist/identity/index.js +0 -54
- package/dist/identity/password-policy.d.ts +0 -57
- package/dist/identity/password-policy.js +0 -66
- package/dist/identity/permission-migrations.d.ts +0 -86
- package/dist/identity/permission-migrations.js +0 -67
- package/dist/identity/permissions.d.ts +0 -35
- package/dist/identity/permissions.js +0 -603
- package/dist/identity/ports.d.ts +0 -201
- package/dist/identity/repo.memory.d.ts +0 -164
- package/dist/identity/repo.memory.js +0 -224
- package/dist/identity/seed.d.ts +0 -54
- package/dist/identity/seed.js +0 -429
- package/dist/identity/tool-registrations.d.ts +0 -50
- package/dist/identity/tool-registrations.js +0 -419
- package/dist/identity/types.d.ts +0 -185
- package/dist/identity/types.js +0 -64
- package/dist/identity/username.d.ts +0 -12
- package/dist/identity/username.js +0 -14
- /package/dist/{identity → media/import}/ports.js +0 -0
|
@@ -30,41 +30,45 @@ import { ToolInputError } from "@jini-ai/core";
|
|
|
30
30
|
* marker above.
|
|
31
31
|
*/
|
|
32
32
|
export class ForbiddenError extends ToolInputError {
|
|
33
|
-
constructor(message) {
|
|
34
|
-
super(message);
|
|
33
|
+
constructor({ message }, _optional = {}) {
|
|
34
|
+
super({ message });
|
|
35
35
|
this.name = "ForbiddenError";
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
38
|
+
/** guard 1 — `key`/field-name grammar gate `^[a-z][a-z0-9_]{0,63}$`. See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
39
39
|
export class InvalidKeyGrammarError extends ToolInputError {
|
|
40
|
-
constructor(
|
|
41
|
-
|
|
40
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
41
|
+
const { message } = requiredArgs;
|
|
42
|
+
super({ message: message });
|
|
42
43
|
this.name = "InvalidKeyGrammarError";
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
/**
|
|
46
|
+
/** guard 2 — `key` is one of the permanently reserved legacy `posts` keys. See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
46
47
|
export class ReservedContentTypeKeyError extends ToolInputError {
|
|
47
|
-
constructor(
|
|
48
|
-
|
|
48
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
49
|
+
const { message } = requiredArgs;
|
|
50
|
+
super({ message: message });
|
|
49
51
|
this.name = "ReservedContentTypeKeyError";
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
|
-
/**
|
|
54
|
+
/** guard 3 — a field name fails the identifier grammar gate. See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
53
55
|
export class InvalidFieldNameGrammarError extends ToolInputError {
|
|
54
|
-
constructor(
|
|
55
|
-
|
|
56
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
57
|
+
const { message } = requiredArgs;
|
|
58
|
+
super({ message: message });
|
|
56
59
|
this.name = "InvalidFieldNameGrammarError";
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
|
-
/**
|
|
62
|
+
/** guard 4 — a field `kind` is not one of the closed field-kind enum. See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
60
63
|
export class InvalidFieldKindError extends ToolInputError {
|
|
61
|
-
constructor(
|
|
62
|
-
|
|
64
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
65
|
+
const { message } = requiredArgs;
|
|
66
|
+
super({ message: message });
|
|
63
67
|
this.name = "InvalidFieldKindError";
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
70
|
/**
|
|
67
|
-
*
|
|
71
|
+
* guard 4b — a field declares a STORAGE-ONLY `kind` (`json`) together with
|
|
68
72
|
* `queryable: true`. The kind itself is legal and the field may be declared and written; what is
|
|
69
73
|
* rejected is the pairing, because a storage-only kind has no `CAST` target and therefore no index
|
|
70
74
|
* to provision.
|
|
@@ -77,10 +81,12 @@ export class InvalidFieldKindError extends ToolInputError {
|
|
|
77
81
|
* `400 VALIDATION_ERROR` with no coordinated change, and the relationship is honest — this IS a
|
|
78
82
|
* rejection of the field's kind, in the context the kind was used. `.name` still distinguishes it
|
|
79
83
|
* for any caller that wants the finer signal.
|
|
84
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
80
85
|
*/
|
|
81
86
|
export class StorageOnlyFieldNotQueryableError extends InvalidFieldKindError {
|
|
82
|
-
constructor(
|
|
83
|
-
|
|
87
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
88
|
+
const { message } = requiredArgs;
|
|
89
|
+
super({ message: message });
|
|
84
90
|
this.name = "StorageOnlyFieldNotQueryableError";
|
|
85
91
|
}
|
|
86
92
|
}
|
|
@@ -89,7 +95,7 @@ export class StorageOnlyFieldNotQueryableError extends InvalidFieldKindError {
|
|
|
89
95
|
* shape the `ContentTypeFieldDef[]` contract does not describe at all (a non-array, a non-object
|
|
90
96
|
* element, a missing/mistyped `name`/`required`/`queryable`, or an unrecognized key).
|
|
91
97
|
*
|
|
92
|
-
* Distinct from the five
|
|
98
|
+
* Distinct from the five guard errors on purpose: those judge a well-formed field
|
|
93
99
|
* definition against a domain rule, and they remain the sole owners of the grammar, kind-enum and
|
|
94
100
|
* queryable-cap decisions. This one fires strictly earlier, at the untrusted-input boundary
|
|
95
101
|
* (`field-defs.ts`), for payloads the guards could not have judged without either crashing or
|
|
@@ -97,33 +103,37 @@ export class StorageOnlyFieldNotQueryableError extends InvalidFieldKindError {
|
|
|
97
103
|
*
|
|
98
104
|
* `violation.received` names the offending value's TYPE, never the value — a field payload can
|
|
99
105
|
* carry operator content, and this message reaches both an HTTP client and a model.
|
|
106
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
100
107
|
*/
|
|
101
108
|
export class InvalidFieldShapeError extends ToolInputError {
|
|
102
109
|
code = "VALIDATION_ERROR";
|
|
103
110
|
violation;
|
|
104
111
|
constructor(violation) {
|
|
105
|
-
super(`${violation.path} must be ${violation.expected}, received ${violation.received}`);
|
|
112
|
+
super({ message: `${violation.path} must be ${violation.expected}, received ${violation.received}` });
|
|
106
113
|
this.name = "InvalidFieldShapeError";
|
|
107
114
|
this.violation = violation;
|
|
108
115
|
}
|
|
109
116
|
}
|
|
110
|
-
/**
|
|
117
|
+
/** guard 5 — more than the per-type cap of `queryable` fields were submitted. See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
111
118
|
export class QueryableFieldCapExceededError extends ToolInputError {
|
|
112
|
-
constructor(
|
|
113
|
-
|
|
119
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
120
|
+
const { message } = requiredArgs;
|
|
121
|
+
super({ message: message });
|
|
114
122
|
this.name = "QueryableFieldCapExceededError";
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
|
-
/**
|
|
125
|
+
/** — `expectedVersion` did not match the current row's version (OCC conflict). See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
118
126
|
export class VersionConflictError extends ToolInputError {
|
|
119
|
-
constructor(
|
|
120
|
-
|
|
127
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
128
|
+
const { message } = requiredArgs;
|
|
129
|
+
super({ message: message });
|
|
121
130
|
this.name = "VersionConflictError";
|
|
122
131
|
}
|
|
123
132
|
}
|
|
124
133
|
export class ContentTypeNotFoundError extends ToolInputError {
|
|
125
|
-
constructor(
|
|
126
|
-
|
|
134
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
135
|
+
const { message } = requiredArgs;
|
|
136
|
+
super({ message: message });
|
|
127
137
|
this.name = "CONTENT_TYPE_NOT_FOUND";
|
|
128
138
|
}
|
|
129
139
|
}
|
|
@@ -131,17 +141,20 @@ export class ContentTypeNotFoundError extends ToolInputError {
|
|
|
131
141
|
* Row 7 (web-high fix plan, 2026-09-24) — `registerContentType` loaded no existing row before
|
|
132
142
|
* saving, so two concurrent first-widget creates (or a caller retrying an existing key) could
|
|
133
143
|
* silently overwrite the current fields, and registering a tombstoned key could resurrect it
|
|
134
|
-
* (
|
|
144
|
+
* ( forbids any transition out of tombstone, including via a fresh register). Thrown when
|
|
135
145
|
* `deps.repo.findByKey` already returns a row for the key, whether that row is active/deprecated
|
|
136
146
|
* or tombstoned — `tombstoned` distinguishes the two so a caller/HTTP route can give the right
|
|
137
147
|
* guidance (retry with update-fields, vs. the key is permanently gone).
|
|
148
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
138
149
|
*/
|
|
139
150
|
export class ContentTypeAlreadyExistsError extends ToolInputError {
|
|
140
151
|
tombstoned;
|
|
141
|
-
constructor(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
153
|
+
const { key, tombstoned } = requiredArgs;
|
|
154
|
+
super({ message: tombstoned
|
|
155
|
+
// See docs/decisions/DR-001-safe-schema-and-index-transitions.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
|
|
156
|
+
? `content type '${key}' was permanently deleted; its key can't be reused`
|
|
157
|
+
: `content type '${key}' already exists; use collections_content_type_update_fields to change its fields` });
|
|
145
158
|
this.name = "ContentTypeAlreadyExistsError";
|
|
146
159
|
this.tombstoned = tombstoned;
|
|
147
160
|
}
|
|
@@ -150,23 +163,27 @@ export class ContentTypeAlreadyExistsError extends ToolInputError {
|
|
|
150
163
|
export class ValidationError extends ToolInputError {
|
|
151
164
|
code = "VALIDATION_ERROR";
|
|
152
165
|
details;
|
|
153
|
-
constructor(
|
|
154
|
-
|
|
166
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
167
|
+
const { message, reason } = requiredArgs;
|
|
168
|
+
super({ message: message });
|
|
155
169
|
this.name = "VALIDATION_ERROR";
|
|
156
170
|
this.details = { reason };
|
|
157
171
|
}
|
|
158
172
|
}
|
|
159
|
-
|
|
173
|
+
/**..12 lifecycle state-machine guard rejection ( terminal-tombstone, deprecate-first). See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
160
174
|
export class ContentTypeLifecycleError extends ToolInputError {
|
|
161
|
-
constructor(
|
|
162
|
-
|
|
175
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
176
|
+
const { message } = requiredArgs;
|
|
177
|
+
super({ message: message });
|
|
163
178
|
this.name = "ContentTypeLifecycleError";
|
|
164
179
|
}
|
|
165
180
|
}
|
|
166
|
-
/**
|
|
181
|
+
/** `planCleanup` eligibility-gate rejection; `reason` is one of a closed, ordered set. See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
167
182
|
export class CleanupNotEligibleError extends Error {
|
|
168
183
|
reason;
|
|
169
|
-
constructor(
|
|
184
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
185
|
+
const { reason } = requiredArgs;
|
|
186
|
+
const { message } = optionalArgs;
|
|
170
187
|
super(message ?? `cleanup is not eligible: ${reason}`);
|
|
171
188
|
this.name = "CleanupNotEligibleError";
|
|
172
189
|
this.reason = reason;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Result } from "@jini-ai/core/primitives";
|
|
2
|
+
import { type ContentTypeFieldDef } from "./types.js";
|
|
2
3
|
/**
|
|
3
4
|
* Validates an untrusted value against the `ContentTypeFieldDef[]` contract, returning either the
|
|
4
5
|
* verified array or the first structural violation found.
|
|
5
6
|
*
|
|
6
7
|
* Stops at the first violation rather than collecting all of them, matching the
|
|
7
|
-
* stop-at-first-failure convention
|
|
8
|
+
* stop-at-first-failure convention already establishes for the domain guard chain
|
|
8
9
|
* this function feeds.
|
|
9
10
|
*
|
|
10
11
|
* @param value - The untrusted `fields` payload: a parsed JSON body's property, or an agent tool's
|
|
@@ -17,9 +18,12 @@ import { type ContentTypeFieldDef, type Result } from "./types.js";
|
|
|
17
18
|
* @complexity O(f·k) time with f = element count (bounded by {@link MAX_FIELD_DEFS}) and k = 4
|
|
18
19
|
* fixed keys, so effectively O(f); O(f) space for the returned array.
|
|
19
20
|
* @example
|
|
20
|
-
* const parsed = parseContentTypeFieldDefs([{ name: "servings", kind: "integer", required: false, queryable: true }]);
|
|
21
|
+
* const parsed = parseContentTypeFieldDefs({ value: [{ name: "servings", kind: "integer", required: false, queryable: true }] });
|
|
21
22
|
* if (!parsed.ok) return { ok: false, error: parsed.error };
|
|
22
23
|
* @overallScore 100
|
|
24
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
23
25
|
*/
|
|
24
|
-
export declare function parseContentTypeFieldDefs(
|
|
26
|
+
export declare function parseContentTypeFieldDefs(requiredArgs: {
|
|
27
|
+
value: unknown;
|
|
28
|
+
}, optionalArgs?: Record<string, never>): Result<ContentTypeFieldDef[], Error>;
|
|
25
29
|
//# sourceMappingURL=field-defs.d.ts.map
|
|
@@ -10,22 +10,22 @@
|
|
|
10
10
|
* validation, which left three real gaps:
|
|
11
11
|
*
|
|
12
12
|
* 1. `required` and `queryable` were never type-checked anywhere in the codebase, so
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* `queryable: "yes"` counted as queryable (`write-service.ts`'s `countQueryableFields` filters
|
|
14
|
+
* on truthiness) and `required: "maybe"` was persisted verbatim into the stored record and its
|
|
15
|
+
* revision `stateJson`. That is a data-integrity gap, not merely a sloppy contract.
|
|
16
16
|
* 2. A non-object element (`fields: [null]`) made the field-name grammar guard throw a bare
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* `TypeError` on `field.name` instead of returning a typed rejection — a 500 on the HTTP path
|
|
18
|
+
* and an opaque `'failed'` tool execution on the agent path.
|
|
19
19
|
* 3. A misspelled key (`queryible: true`) was silently dropped rather than reported, so a caller
|
|
20
|
-
*
|
|
20
|
+
* got a field that was quietly non-queryable with no indication why.
|
|
21
21
|
*
|
|
22
22
|
* Division of responsibility (deliberate, and the reason this module is narrow):
|
|
23
23
|
* this module owns STRUCTURE only — array-ness, element object-ness, the four keys and their
|
|
24
24
|
* types. It does NOT own the domain rules. The field-name grammar gate, the reserved-key check and
|
|
25
|
-
* the queryable-field cap stay solely in `write-service.ts`'s
|
|
25
|
+
* the queryable-field cap stay solely in `write-service.ts`'s guard chain, whose
|
|
26
26
|
* fixed order (`key grammar -> reserved-key -> field-name grammar -> field-kind -> queryable-cap`)
|
|
27
27
|
* and stop-at-first-failure behavior are pinned by
|
|
28
|
-
* `__tests__/unit/write-service.register.unit.test.ts
|
|
28
|
+
* `__tests__/unit/write-service.register.unit.test.ts`. Duplicating any of those here
|
|
29
29
|
* would either reorder that chain for boundary callers or create a second source of truth for a
|
|
30
30
|
* rule that must have exactly one definition.
|
|
31
31
|
*
|
|
@@ -38,8 +38,10 @@
|
|
|
38
38
|
* Architectural role:
|
|
39
39
|
* `features/content-types` domain logic. Pure — no I/O, no clock, no repo. Depends only on this
|
|
40
40
|
* package's own `types.ts` and `errors.ts`.
|
|
41
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
41
42
|
*/
|
|
42
43
|
import { InvalidFieldKindError, InvalidFieldShapeError } from "./errors.js";
|
|
44
|
+
import {} from "@jini-ai/core/primitives";
|
|
43
45
|
import { isContentTypeFieldKind } from "./types.js";
|
|
44
46
|
/**
|
|
45
47
|
* The complete, closed key set of a `ContentTypeFieldDef`. Any other key is rejected rather than
|
|
@@ -78,7 +80,7 @@ function isPlainObject(value) {
|
|
|
78
80
|
* verified array or the first structural violation found.
|
|
79
81
|
*
|
|
80
82
|
* Stops at the first violation rather than collecting all of them, matching the
|
|
81
|
-
* stop-at-first-failure convention
|
|
83
|
+
* stop-at-first-failure convention already establishes for the domain guard chain
|
|
82
84
|
* this function feeds.
|
|
83
85
|
*
|
|
84
86
|
* @param value - The untrusted `fields` payload: a parsed JSON body's property, or an agent tool's
|
|
@@ -91,11 +93,13 @@ function isPlainObject(value) {
|
|
|
91
93
|
* @complexity O(f·k) time with f = element count (bounded by {@link MAX_FIELD_DEFS}) and k = 4
|
|
92
94
|
* fixed keys, so effectively O(f); O(f) space for the returned array.
|
|
93
95
|
* @example
|
|
94
|
-
* const parsed = parseContentTypeFieldDefs([{ name: "servings", kind: "integer", required: false, queryable: true }]);
|
|
96
|
+
* const parsed = parseContentTypeFieldDefs({ value: [{ name: "servings", kind: "integer", required: false, queryable: true }] });
|
|
95
97
|
* if (!parsed.ok) return { ok: false, error: parsed.error };
|
|
96
98
|
* @overallScore 100
|
|
99
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
97
100
|
*/
|
|
98
|
-
export function parseContentTypeFieldDefs(
|
|
101
|
+
export function parseContentTypeFieldDefs(requiredArgs, optionalArgs = {}) {
|
|
102
|
+
const { value } = requiredArgs;
|
|
99
103
|
if (!Array.isArray(value)) {
|
|
100
104
|
return { ok: false, error: new InvalidFieldShapeError({ path: "fields", expected: "an array", received: describeType(value) }) };
|
|
101
105
|
}
|
|
@@ -147,9 +151,10 @@ function parseOneFieldDef(element, at) {
|
|
|
147
151
|
if (typeof element.kind !== "string") {
|
|
148
152
|
return { ok: false, error: new InvalidFieldShapeError({ path: `${at}.kind`, expected: "a string", received: describeType(element.kind) }) };
|
|
149
153
|
}
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
const kind = { value: element.kind };
|
|
155
|
+
if (!isContentTypeFieldKind(kind)) {
|
|
156
|
+
return { ok: false, error: new InvalidFieldKindError({ message: `field '${element.name}' has kind '${element.kind}', not one of the closed field-kind enum` }) };
|
|
152
157
|
}
|
|
153
|
-
return { ok: true, value: { name: element.name, kind:
|
|
158
|
+
return { ok: true, value: { name: element.name, kind: kind.value, required: element.required, queryable: element.queryable } };
|
|
154
159
|
}
|
|
155
160
|
//# sourceMappingURL=field-defs.js.map
|
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
import { type ContentTypeFieldKind, type IndexableFieldKind } from "./types.js";
|
|
2
2
|
/**
|
|
3
|
-
* @file
|
|
3
|
+
* @file the `kind`->`CAST` fixed lookup table, the identifier grammar
|
|
4
4
|
* gate, the workspace-scoped queryable-index naming scheme, and the before/after index-transition
|
|
5
5
|
* resolver (grammar-and-workspace-scoping folds).
|
|
6
6
|
*
|
|
7
7
|
* Purpose:
|
|
8
8
|
* THIS IS THE HIGHEST-SECURITY-SEVERITY MODULE IN THE 5-PACKAGE PIPELINE — every value that ends
|
|
9
|
-
* up inside a `CREATE INDEX
|
|
9
|
+
* up inside a `CREATE INDEX... CAST(json_extract(fields,'$.ext.{ns}.{field}') AS {type})`
|
|
10
10
|
* statement is produced here, and only here:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* - `mapFieldKindToCast`. STATE THE INVARIANT PRECISELY, because an earlier version
|
|
12
|
+
* of this comment did not and the imprecision was itself a hazard: it said "a fixed, hardcoded
|
|
13
|
+
* 5-entry table", which reads as though the ENTRY COUNT were the defense. It is not. Neither
|
|
14
|
+
* the number of keys nor the number of distinct values is the security boundary.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* THE DEFENSE IS: this function returns a VALUE FROM THE TABLE and never returns, echoes, or
|
|
17
|
+
* interpolates its own ARGUMENT. The function has exactly one `return`, and it is a table
|
|
18
|
+
* lookup. Therefore the set of strings that can reach a `CAST(... AS {type})` position is
|
|
19
|
+
* exactly the set of string literals written into `KIND_TO_CAST_LITERAL` in this source file —
|
|
20
|
+
* compile-time constants, every one — no matter what an operator, an agent, or an attacker
|
|
21
|
+
* supplies as `kind`. A 50-entry table of literals would be exactly as safe as a 5-entry one.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
23
|
+
* What follows from that, and what a maintainer actually needs to know:
|
|
24
|
+
* * Adding a KEY whose value is an existing literal cannot widen the DDL alphabet. It is not
|
|
25
|
+
* a security event and does not need to be feared as one. (`relation: "TEXT"` is exactly
|
|
26
|
+
* this: `"TEXT"` was already present twice.)
|
|
27
|
+
* * Adding a new VALUE does widen it, and must be reviewed as a DDL change.
|
|
28
|
+
* * Returning, concatenating, or template-interpolating `kind` itself — or reading the table
|
|
29
|
+
* with a fallback such as `?? kind` — BREAKS the invariant outright. That is the edit to
|
|
30
|
+
* refuse, and it is the one the old "5-entry" phrasing gave no way to recognise.
|
|
31
|
+
* The reachable-value set is pinned executably by `HISTORICAL_DDL_ALPHABET` in
|
|
32
|
+
* `__tests__/field-kind-widening.test.ts`, so this claim is a failing test and not only prose.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
34
|
+
* Degraded failure mode, stated so the blast radius is known: if every runtime guard below
|
|
35
|
+
* were deleted, a storage-only kind would reach the table and `KIND_TO_CAST_LITERAL["json"]`
|
|
36
|
+
* would evaluate to `undefined` — a SQL syntax error and a loud broken feature, never an
|
|
37
|
+
* operator-controlled string. Total guard removal degrades this module to unavailable, not to
|
|
38
|
+
* injectable. That is the property worth preserving through any future change.
|
|
39
|
+
* - `validateIdentifierGrammar`/`buildQueryableFieldIndexName` gate every `key`/field name
|
|
40
|
+
* through `^[a-z][a-z0-9_]{0,63}$` before it can reach an index-name segment or JSON-path
|
|
41
|
+
* literal, and join grammar-gated segments with `/` — a delimiter outside the
|
|
42
|
+
* grammar's own alphabet `[a-z0-9_]`, closing the namespace-injectivity collision class
|
|
43
|
+
* — and fold in a workspace-derived segment so two workspaces defining the same
|
|
44
|
+
* `(key, field)` pair never collide on index identity.
|
|
45
45
|
*
|
|
46
46
|
* How it relates to the project:
|
|
47
47
|
* `resolveFieldIndexTransition` deliberately still ACCEPTS a storage-only kind on its `none` and
|
|
@@ -51,35 +51,40 @@ import { type ContentTypeFieldKind, type IndexableFieldKind } from "./types.js";
|
|
|
51
51
|
*
|
|
52
52
|
* `write-service.ts`'s `registerContentType`/`updateContentTypeFields` call
|
|
53
53
|
* `resolveFieldIndexTransition` once per field (never two independent kind/queryable branches —
|
|
54
|
-
*
|
|
54
|
+
* ) and hand the result to the injected `indexProvisioner` port, which is the only
|
|
55
55
|
* thing that ever issues the real `CREATE INDEX`/`DROP INDEX` DDL (this module only decides what
|
|
56
56
|
* to build, never executes DDL itself).
|
|
57
57
|
*
|
|
58
58
|
* Architectural role:
|
|
59
|
-
* `features/content-types` domain logic.
|
|
60
|
-
*
|
|
59
|
+
* `features/content-types` domain logic. Workspace-segment hashing uses Web Crypto so this
|
|
60
|
+
* universal entry needs only standard runtime APIs and this package's own `errors.ts`/`types.ts`.
|
|
61
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
61
62
|
*/
|
|
62
63
|
/**
|
|
63
|
-
*
|
|
64
|
+
* — the closed identifier grammar every `content_types.key` and field name must satisfy,
|
|
64
65
|
* as a pattern string so the agent-facing JSON Schemas in `agent-tools.ts` can publish the very
|
|
65
66
|
* same grammar instead of restating it. A cross-cutting single-source-of-truth governance rule makes this grammar load-bearing for DDL
|
|
66
67
|
* safety, so it must have exactly one definition; {@link IDENTIFIER_GRAMMAR} is compiled from this
|
|
67
68
|
* string rather than written twice.
|
|
69
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
68
70
|
*/
|
|
69
71
|
export declare const IDENTIFIER_GRAMMAR_PATTERN = "^[a-z][a-z0-9_]{0,63}$";
|
|
70
72
|
/**
|
|
71
|
-
* Structural grammar gate for a `content_types.key` or field name
|
|
73
|
+
* Structural grammar gate for a `content_types.key` or field name.
|
|
72
74
|
*
|
|
73
75
|
* @complexity O(n) in the string's length (regex match), bounded at 64 chars.
|
|
74
76
|
* @overallScore 100
|
|
77
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
75
78
|
*/
|
|
76
|
-
export declare function validateIdentifierGrammar(
|
|
79
|
+
export declare function validateIdentifierGrammar(requiredArgs: {
|
|
80
|
+
value: string;
|
|
81
|
+
}, optionalArgs?: Record<string, never>): boolean;
|
|
77
82
|
/**
|
|
78
83
|
* Maps an INDEXABLE field `kind` to its fixed `CAST(... AS {type})` type token. Throws
|
|
79
84
|
* {@link InvalidFieldKindError} for anything that is not exactly one of `INDEXABLE_FIELD_KINDS` —
|
|
80
85
|
* including a storage-only kind such as `json`, which is a legal kind to declare but has no CAST
|
|
81
86
|
* target. This is a lookup, never a template, so an adversarial payload can never reach the
|
|
82
|
-
* returned string
|
|
87
|
+
* returned string.
|
|
83
88
|
*
|
|
84
89
|
* The PARAMETER type is deliberately the wider `ContentTypeFieldKind`. Narrowing it would be a
|
|
85
90
|
* breaking change for `@jini-ai/cms`'s public consumers (`content-types/index.ts` documents this
|
|
@@ -93,23 +98,31 @@ export declare function validateIdentifierGrammar(value: string): boolean;
|
|
|
93
98
|
*
|
|
94
99
|
* @complexity O(1) — a fixed-size object lookup behind a fixed-size membership test.
|
|
95
100
|
* @overallScore 100
|
|
101
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
96
102
|
*/
|
|
97
|
-
export declare function mapFieldKindToCast(
|
|
103
|
+
export declare function mapFieldKindToCast(requiredArgs: {
|
|
104
|
+
kind: ContentTypeFieldKind;
|
|
105
|
+
}, optionalArgs?: Record<string, never>): string;
|
|
98
106
|
/**
|
|
99
107
|
* Builds the workspace-scoped queryable-field index identity for `(workspaceId, contentTypeKey,
|
|
100
108
|
* fieldName)`. Both `contentTypeKey` and `fieldName` are grammar-gated first — a failure throws
|
|
101
|
-
* before any index name is produced
|
|
109
|
+
* before any index name is produced. Segments are joined with `/`, a delimiter outside
|
|
102
110
|
* the grammar's own alphabet `[a-z0-9_]`, so distinct `(key, name)` pairs can never collide by
|
|
103
|
-
* boundary-shifting (
|
|
111
|
+
* boundary-shifting (e.g. `("a_b","c")` vs `("a","b_c")`).
|
|
112
|
+
*
|
|
113
|
+
* Returns a promise for the unchanged index identity. Grammar failures still throw
|
|
114
|
+
* synchronously before hashing; Web Crypto failures reject the promise. Callers must await
|
|
115
|
+
* the name before using it in DDL.
|
|
104
116
|
*
|
|
105
|
-
* @complexity O(
|
|
117
|
+
* @complexity O(n) time and space in the UTF-8 workspace ID length, plus two grammar checks.
|
|
106
118
|
* @overallScore 100
|
|
119
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
107
120
|
*/
|
|
108
121
|
export declare function buildQueryableFieldIndexName(params: {
|
|
109
122
|
workspaceId: string;
|
|
110
123
|
contentTypeKey: string;
|
|
111
124
|
fieldName: string;
|
|
112
|
-
}): string
|
|
125
|
+
}): Promise<string>;
|
|
113
126
|
/** A field's `(kind, queryable)` pair at one side of an update call, or `undefined` if absent (new/removed). */
|
|
114
127
|
export type FieldIndexState = {
|
|
115
128
|
kind: ContentTypeFieldKind;
|
|
@@ -125,13 +138,14 @@ export interface FieldIndexTransition {
|
|
|
125
138
|
newKind?: IndexableFieldKind;
|
|
126
139
|
}
|
|
127
140
|
/**
|
|
128
|
-
*
|
|
141
|
+
* — resolves a single field's index transition from ONE before/after comparison of
|
|
129
142
|
* its `(kind, queryable)` pair, never from two independent branches that each assume the other
|
|
130
143
|
* property is unchanged (the bug class this closes: a field whose `kind` AND `queryable` both
|
|
131
|
-
* change in the same call must still resolve correctly —
|
|
144
|
+
* change in the same call must still resolve correctly — (b)).
|
|
132
145
|
*
|
|
133
146
|
* @complexity O(1) — four comparisons, one of four fixed outcomes.
|
|
134
147
|
* @overallScore 100
|
|
148
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
135
149
|
*/
|
|
136
150
|
export declare function resolveFieldIndexTransition(params: {
|
|
137
151
|
before: FieldIndexState;
|