@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
|
@@ -25,52 +25,59 @@
|
|
|
25
25
|
* their own repo lookups, so the ordering guarantee holds regardless of how a caller wires them.
|
|
26
26
|
*/
|
|
27
27
|
export class TaxonomyNotApplicableError extends Error {
|
|
28
|
-
constructor(
|
|
28
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
29
|
+
const { message } = requiredArgs;
|
|
29
30
|
super(message);
|
|
30
31
|
this.name = "TaxonomyNotApplicableError";
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
export class WorkspaceMismatchError extends Error {
|
|
34
|
-
constructor(
|
|
35
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
36
|
+
const { message } = requiredArgs;
|
|
35
37
|
super(message);
|
|
36
38
|
this.name = "WorkspaceMismatchError";
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
export class ContentTypeMismatchError extends Error {
|
|
40
|
-
constructor(
|
|
42
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
43
|
+
const { message } = requiredArgs;
|
|
41
44
|
super(message);
|
|
42
45
|
this.name = "ContentTypeMismatchError";
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
export class TaxonomyNotHierarchicalError extends Error {
|
|
46
|
-
constructor(
|
|
49
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
50
|
+
const { message } = requiredArgs;
|
|
47
51
|
super(message);
|
|
48
52
|
this.name = "TaxonomyNotHierarchicalError";
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
export class ParentCrossTaxonomyError extends Error {
|
|
52
|
-
constructor(
|
|
56
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
57
|
+
const { message } = requiredArgs;
|
|
53
58
|
super(message);
|
|
54
59
|
this.name = "ParentCrossTaxonomyError";
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
export class TermNotFoundError extends Error {
|
|
58
|
-
constructor(
|
|
63
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
64
|
+
const { message } = requiredArgs;
|
|
59
65
|
super(message);
|
|
60
66
|
this.name = "TermNotFoundError";
|
|
61
67
|
}
|
|
62
68
|
}
|
|
63
69
|
export class HierarchyCycleDetectedError extends Error {
|
|
64
|
-
constructor(
|
|
70
|
+
constructor(requiredArgs, optionalArgs = {}) {
|
|
71
|
+
const { message } = requiredArgs;
|
|
65
72
|
super(message);
|
|
66
73
|
this.name = "HierarchyCycleDetectedError";
|
|
67
74
|
}
|
|
68
75
|
}
|
|
69
76
|
/**
|
|
70
|
-
*
|
|
77
|
+
* — true if assigning `candidateParentId` as `termId`'s parent would create a cycle:
|
|
71
78
|
* either immediate self-parenting, or `termId` appearing anywhere in `candidateParentId`'s own
|
|
72
79
|
* ancestor chain. A full recursive walk, not a fixed-depth or immediate-parent-only comparison
|
|
73
|
-
*
|
|
80
|
+
* — the exact shape a naive implementation gets wrong (a 3+-hop cycle is the
|
|
74
81
|
* regression case this exists to catch).
|
|
75
82
|
*
|
|
76
83
|
* A `visited` guard bounds the walk even against a pre-existing malformed tree (defensive; no
|
|
@@ -79,6 +86,7 @@ export class HierarchyCycleDetectedError extends Error {
|
|
|
79
86
|
*
|
|
80
87
|
* @complexity O(depth) — one `getParentId` call per ancestor hop, each hop visited at most once.
|
|
81
88
|
* @overallScore 100
|
|
89
|
+
* See docs/decisions/DR-005-ordered-taxonomy-validation.md.
|
|
82
90
|
*/
|
|
83
91
|
export function wouldCreateCycle(required, _optional = {}) {
|
|
84
92
|
const { termId, candidateParentId, tree } = required;
|
|
@@ -90,7 +98,7 @@ export function wouldCreateCycle(required, _optional = {}) {
|
|
|
90
98
|
if (visited.has(current))
|
|
91
99
|
return false; // malformed/self-looping ancestor data upstream of termId; not this call's cycle
|
|
92
100
|
visited.add(current);
|
|
93
|
-
const parent = tree.getParentId(current);
|
|
101
|
+
const parent = tree.getParentId({ termId: current });
|
|
94
102
|
if (parent === termId)
|
|
95
103
|
return true;
|
|
96
104
|
current = parent;
|
|
@@ -98,55 +106,57 @@ export function wouldCreateCycle(required, _optional = {}) {
|
|
|
98
106
|
return false;
|
|
99
107
|
}
|
|
100
108
|
/**
|
|
101
|
-
*
|
|
102
|
-
* fixed order. A combined-failure case always reports the first-in-order failure (
|
|
103
|
-
* not-on-allow-list always wins over a simultaneous workspace mismatch;
|
|
109
|
+
* /ORD1/ORD2 — the content-join chain: allow-list, THEN workspace, THEN lens, in that
|
|
110
|
+
* fixed order. A combined-failure case always reports the first-in-order failure (:
|
|
111
|
+
* not-on-allow-list always wins over a simultaneous workspace mismatch; : a workspace
|
|
104
112
|
* mismatch always wins over a simultaneous lens mismatch).
|
|
105
113
|
*
|
|
106
114
|
* @complexity O(1) — three independent comparisons, no loop.
|
|
107
115
|
* @overallScore 100
|
|
116
|
+
* See docs/decisions/DR-005-ordered-taxonomy-validation.md.
|
|
108
117
|
*/
|
|
109
118
|
export function validateContentJoin(required, _optional = {}) {
|
|
110
119
|
const { taxonomyId, isOnAllowList, callerWorkspaceId, resolvedTermWorkspaceId, resolvedContentWorkspaceId, suppliedContentType, resolvedContentKind, } = required;
|
|
111
120
|
if (!isOnAllowList) {
|
|
112
|
-
throw new TaxonomyNotApplicableError(`taxonomy '${taxonomyId}' is not on the allow-list for content type '${suppliedContentType}'`);
|
|
121
|
+
throw new TaxonomyNotApplicableError({ message: `taxonomy '${taxonomyId}' is not on the allow-list for content type '${suppliedContentType}'` });
|
|
113
122
|
}
|
|
114
123
|
if (resolvedTermWorkspaceId !== callerWorkspaceId || resolvedContentWorkspaceId !== callerWorkspaceId) {
|
|
115
|
-
throw new WorkspaceMismatchError(`term/content workspace does not match the caller's workspace '${callerWorkspaceId}'`);
|
|
124
|
+
throw new WorkspaceMismatchError({ message: `term/content workspace does not match the caller's workspace '${callerWorkspaceId}'` });
|
|
116
125
|
}
|
|
117
126
|
if (resolvedContentKind !== suppliedContentType) {
|
|
118
|
-
throw new ContentTypeMismatchError(`supplied content type '${suppliedContentType}' does not match the resolved row's own kind '${resolvedContentKind}'`);
|
|
127
|
+
throw new ContentTypeMismatchError({ message: `supplied content type '${suppliedContentType}' does not match the resolved row's own kind '${resolvedContentKind}'` });
|
|
119
128
|
}
|
|
120
129
|
}
|
|
121
130
|
/**
|
|
122
|
-
*
|
|
131
|
+
* /B3/ORD3 — the hierarchy chain: hierarchical-mode, THEN same-taxonomy (distinguishing
|
|
123
132
|
* not-found from wrong-taxonomy), THEN cycle, in that fixed order. `null` `candidateParentId`
|
|
124
|
-
* (no parent assignment attempted) always short-circuits to a pass regardless of mode (
|
|
133
|
+
* (no parent assignment attempted) always short-circuits to a pass regardless of mode (
|
|
125
134
|
* regression: a no-op parent write must never be rejected).
|
|
126
135
|
*
|
|
127
|
-
* The compound
|
|
136
|
+
* The compound regression this chain exists to prevent: a flat taxonomy's candidate parent
|
|
128
137
|
* that ALSO happens to exist in a different taxonomy must report `TAXONOMY_NOT_HIERARCHICAL`,
|
|
129
138
|
* never `PARENT_CROSS_TAXONOMY` — hierarchical-mode is checked first and is decisive on its own.
|
|
130
139
|
*
|
|
131
|
-
* @complexity O(1) plus the injected `wouldCreateCycle` closure's own cost (
|
|
140
|
+
* @complexity O(1) plus the injected `wouldCreateCycle` closure's own cost (only reached
|
|
132
141
|
* once hierarchical-mode and same-taxonomy both pass).
|
|
133
142
|
* @overallScore 100
|
|
143
|
+
* See docs/decisions/DR-005-ordered-taxonomy-validation.md.
|
|
134
144
|
*/
|
|
135
145
|
export function validateHierarchyAssignment(required, _optional = {}) {
|
|
136
146
|
const { taxonomyIsHierarchical, candidateParentId, resolvedParent, childTaxonomyId, wouldCreateCycle: checkCycle } = required;
|
|
137
147
|
if (candidateParentId === null)
|
|
138
148
|
return;
|
|
139
149
|
if (!taxonomyIsHierarchical) {
|
|
140
|
-
throw new TaxonomyNotHierarchicalError(`taxonomy '${childTaxonomyId}' is not hierarchical; parentId must be null`);
|
|
150
|
+
throw new TaxonomyNotHierarchicalError({ message: `taxonomy '${childTaxonomyId}' is not hierarchical; parentId must be null` });
|
|
141
151
|
}
|
|
142
152
|
if (resolvedParent === null || resolvedParent === "not-applicable") {
|
|
143
|
-
throw new TermNotFoundError(`parent term '${candidateParentId}' was not found`);
|
|
153
|
+
throw new TermNotFoundError({ message: `parent term '${candidateParentId}' was not found` });
|
|
144
154
|
}
|
|
145
155
|
if (resolvedParent.taxonomyId !== childTaxonomyId) {
|
|
146
|
-
throw new ParentCrossTaxonomyError(`parent term '${candidateParentId}' belongs to taxonomy '${resolvedParent.taxonomyId}', not '${childTaxonomyId}'`);
|
|
156
|
+
throw new ParentCrossTaxonomyError({ message: `parent term '${candidateParentId}' belongs to taxonomy '${resolvedParent.taxonomyId}', not '${childTaxonomyId}'` });
|
|
147
157
|
}
|
|
148
|
-
if (checkCycle(candidateParentId)) {
|
|
149
|
-
throw new HierarchyCycleDetectedError(`assigning '${candidateParentId}' as parent would create a hierarchy cycle`);
|
|
158
|
+
if (checkCycle({ candidateParentId })) {
|
|
159
|
+
throw new HierarchyCycleDetectedError({ message: `assigning '${candidateParentId}' as parent would create a hierarchy cycle` });
|
|
150
160
|
}
|
|
151
161
|
}
|
|
152
162
|
//# sourceMappingURL=validation-chain.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Clock, IdGenerator } from "@jini-ai/core/primitives";
|
|
1
2
|
import type { TermListPort } from "./list.js";
|
|
2
3
|
/**
|
|
3
4
|
* @file The taxonomy write-service's ordinary (non-gated) mutations.
|
|
@@ -62,7 +63,9 @@ export interface Term {
|
|
|
62
63
|
version: number;
|
|
63
64
|
}
|
|
64
65
|
export interface TaxonomyRepoPort {
|
|
65
|
-
findById(id
|
|
66
|
+
findById({ id }: {
|
|
67
|
+
id: string;
|
|
68
|
+
}): Promise<{
|
|
66
69
|
id: string;
|
|
67
70
|
hierarchical: boolean;
|
|
68
71
|
allowList?: string[] | undefined;
|
|
@@ -70,7 +73,9 @@ export interface TaxonomyRepoPort {
|
|
|
70
73
|
insert(row: Taxonomy): Promise<unknown>;
|
|
71
74
|
}
|
|
72
75
|
export interface TermRepoPort {
|
|
73
|
-
findById(id
|
|
76
|
+
findById({ id }: {
|
|
77
|
+
id: string;
|
|
78
|
+
}): Promise<{
|
|
74
79
|
id: string;
|
|
75
80
|
taxonomyId: string;
|
|
76
81
|
name?: string | undefined;
|
|
@@ -102,9 +107,11 @@ export interface ContentLookupPort {
|
|
|
102
107
|
kind: string;
|
|
103
108
|
} | null>;
|
|
104
109
|
}
|
|
105
|
-
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
110
|
+
/** : the content_types registry's per-type taxonomy allow-list for Collections entries
|
|
111
|
+
* (parallel to, never folded into, TAXONOMY_ALLOWED_CONTENT_TYPES — see the decision note).
|
|
112
|
+
* `null` = not a live registry type (not applicable).
|
|
113
|
+
* See docs/decisions/DR-005-ordered-taxonomy-validation.md.
|
|
114
|
+
*/
|
|
108
115
|
export interface ContentTypeTaxonomyPolicyPort {
|
|
109
116
|
taxonomiesFor(params: {
|
|
110
117
|
contentType: string;
|
|
@@ -117,7 +124,9 @@ export interface ContentTypeTaxonomyPolicyPort {
|
|
|
117
124
|
* an intentionally parallel (not layered) per-content-type policy carried by a host's own
|
|
118
125
|
* `content_types` registry. */
|
|
119
126
|
export declare const TAXONOMY_ALLOWED_CONTENT_TYPES: ReadonlySet<string>;
|
|
120
|
-
export declare function isContentTypeOnAllowList(
|
|
127
|
+
export declare function isContentTypeOnAllowList(requiredArgs: {
|
|
128
|
+
contentType: string;
|
|
129
|
+
}, optionalArgs?: Record<string, never>): boolean;
|
|
121
130
|
export interface TaxonomyRevisionRow {
|
|
122
131
|
taxonomyId: string;
|
|
123
132
|
/** `"delete"` added for `deleteTerm`/`deleteTaxonomy` below — unlike `mergeTerm`'s reuse of
|
|
@@ -132,16 +141,10 @@ export interface TaxonomyRevisionRow {
|
|
|
132
141
|
export interface TaxonomyRevisionRepoPort {
|
|
133
142
|
insert(row: TaxonomyRevisionRow): Promise<unknown>;
|
|
134
143
|
}
|
|
135
|
-
export interface ClockPort {
|
|
136
|
-
nowIso(): string;
|
|
137
|
-
}
|
|
138
|
-
export interface IdGeneratorPort {
|
|
139
|
-
newId(): string;
|
|
140
|
-
}
|
|
141
144
|
export interface WriteServiceDeps {
|
|
142
145
|
authorize: AuthorizeFn;
|
|
143
|
-
clock:
|
|
144
|
-
idGen:
|
|
146
|
+
clock: Clock;
|
|
147
|
+
idGen: IdGenerator;
|
|
145
148
|
taxonomies: TaxonomyRepoPort;
|
|
146
149
|
terms: TermRepoPort;
|
|
147
150
|
entryTerms: EntryTermRepoPort;
|
|
@@ -149,9 +152,13 @@ export interface WriteServiceDeps {
|
|
|
149
152
|
/** `core/gated-mutations.stampWatermark`-shaped, injected — same-transaction stamp per mutation.
|
|
150
153
|
* May be sync (SQLite) or async (PGlite/Postgres); every call site awaits it, so an async stamp
|
|
151
154
|
* finishes, and its failure fails the write, before the mutation returns. */
|
|
152
|
-
stampWatermark: (
|
|
155
|
+
stampWatermark: (required: Record<string, never>, optional?: {
|
|
156
|
+
tx?: unknown;
|
|
157
|
+
}) => Promise<void> | void;
|
|
153
158
|
outbox: {
|
|
154
|
-
enqueue: (event:
|
|
159
|
+
enqueue: ({ event }: {
|
|
160
|
+
event: unknown;
|
|
161
|
+
}) => Promise<void>;
|
|
155
162
|
};
|
|
156
163
|
/** The caller's own workspace — `validateContentJoin`'s `callerWorkspaceId` (Finding 1 fix). */
|
|
157
164
|
workspaceId: string;
|
|
@@ -164,14 +171,20 @@ export interface WriteServiceDeps {
|
|
|
164
171
|
contentTypeTaxonomyPolicy?: ContentTypeTaxonomyPolicyPort;
|
|
165
172
|
}
|
|
166
173
|
export declare class TaxonomyRecordNotFoundError extends Error {
|
|
167
|
-
constructor(
|
|
174
|
+
constructor(requiredArgs: {
|
|
175
|
+
message: string;
|
|
176
|
+
}, optionalArgs?: Record<string, never>);
|
|
168
177
|
}
|
|
169
178
|
export declare class TermRecordNotFoundError extends Error {
|
|
170
|
-
constructor(
|
|
179
|
+
constructor(requiredArgs: {
|
|
180
|
+
message: string;
|
|
181
|
+
}, optionalArgs?: Record<string, never>);
|
|
171
182
|
}
|
|
172
183
|
/** Finding 1 fix — the target of an `assignTerms` call does not resolve to any real content row. */
|
|
173
184
|
export declare class ContentRecordNotFoundError extends Error {
|
|
174
|
-
constructor(
|
|
185
|
+
constructor(requiredArgs: {
|
|
186
|
+
message: string;
|
|
187
|
+
}, optionalArgs?: Record<string, never>);
|
|
175
188
|
}
|
|
176
189
|
/**
|
|
177
190
|
* J1 (plan-publish-all-types-2026-09-25.md) — `importTaxonomy`/`importTerm`'s optimistic-concurrency
|
|
@@ -182,7 +195,9 @@ export declare class ContentRecordNotFoundError extends Error {
|
|
|
182
195
|
* "catch the one it actually called into" rule.
|
|
183
196
|
*/
|
|
184
197
|
export declare class TaxonomyVersionConflictError extends Error {
|
|
185
|
-
constructor(
|
|
198
|
+
constructor(requiredArgs: {
|
|
199
|
+
message: string;
|
|
200
|
+
}, optionalArgs?: Record<string, never>);
|
|
186
201
|
}
|
|
187
202
|
export interface CreateTaxonomyRequired {
|
|
188
203
|
deps: WriteServiceDeps;
|
|
@@ -198,13 +213,17 @@ export interface CreateTermRequired {
|
|
|
198
213
|
principalId: string;
|
|
199
214
|
taxonomyId: string;
|
|
200
215
|
name: string;
|
|
216
|
+
}
|
|
217
|
+
export interface CreateTermOptional {
|
|
201
218
|
parentId?: string | null | undefined;
|
|
202
219
|
}
|
|
203
|
-
/**
|
|
220
|
+
/** — validates `parentId` via `validateHierarchyAssignment` before writing. A
|
|
204
221
|
* freshly-created term has no descendants yet, so it structurally cannot be a cycle source; the
|
|
205
|
-
* cycle check is therefore always a no-op (`
|
|
206
|
-
* term (not yet built — no certified test in this slice exercises it).
|
|
207
|
-
|
|
222
|
+
* cycle check is therefore always a no-op (` => false`) here, unlike a reparent of an existing
|
|
223
|
+
* term (not yet built — no certified test in this slice exercises it).
|
|
224
|
+
* See docs/decisions/DR-005-ordered-taxonomy-validation.md.
|
|
225
|
+
*/
|
|
226
|
+
export declare function createTerm(required: CreateTermRequired, optional?: CreateTermOptional): Promise<Term>;
|
|
208
227
|
/**
|
|
209
228
|
* Additive capability beyond the certified `TaxonomyRepoPort` — `findById`'s narrow return
|
|
210
229
|
* (`{id, hierarchical, allowList?}`) doesn't carry `version`/`name`/`status`/`updatedAt`, which
|
|
@@ -212,7 +231,9 @@ export declare function createTerm(required: CreateTermRequired, _optional?: Rec
|
|
|
212
231
|
* write an existing row back. Same beyond-the-certified-port precedent as `DeletableTaxonomyRepoPort`.
|
|
213
232
|
*/
|
|
214
233
|
export interface ImportableTaxonomyRepoPort {
|
|
215
|
-
findByIdFull(id
|
|
234
|
+
findByIdFull({ id }: {
|
|
235
|
+
id: string;
|
|
236
|
+
}): Promise<Taxonomy | null>;
|
|
216
237
|
update(row: Taxonomy): Promise<unknown>;
|
|
217
238
|
}
|
|
218
239
|
export interface ImportTaxonomyRequired {
|
|
@@ -249,7 +270,9 @@ export declare function importTaxonomy(required: ImportTaxonomyRequired, _option
|
|
|
249
270
|
* `TermRepoPort` already has `update`, unlike taxonomies, so no additive `update` is needed here.
|
|
250
271
|
*/
|
|
251
272
|
export interface ImportableTermRepoPort {
|
|
252
|
-
findByIdFull(id
|
|
273
|
+
findByIdFull({ id }: {
|
|
274
|
+
id: string;
|
|
275
|
+
}): Promise<Term | null>;
|
|
253
276
|
}
|
|
254
277
|
export interface ImportTermRequired {
|
|
255
278
|
deps: WriteServiceDeps & {
|
|
@@ -260,10 +283,12 @@ export interface ImportTermRequired {
|
|
|
260
283
|
id: string;
|
|
261
284
|
taxonomyId: string;
|
|
262
285
|
name: string;
|
|
263
|
-
parentId?: string | null | undefined;
|
|
264
286
|
/** Same three-way CAS contract as `importTaxonomy.expectedVersion`. */
|
|
265
287
|
expectedVersion: number | undefined;
|
|
266
288
|
}
|
|
289
|
+
export interface ImportTermOptional {
|
|
290
|
+
parentId?: string | null | undefined;
|
|
291
|
+
}
|
|
267
292
|
/**
|
|
268
293
|
* Slice J1 — the publish-content import chokepoint for `terms`. Address (name within
|
|
269
294
|
* `(taxonomyId, parentId)`) uniqueness is the publish factory's own precheck, not this function's
|
|
@@ -277,16 +302,23 @@ export interface ImportTermRequired {
|
|
|
277
302
|
* @complexity O(d) — one taxonomy lookup, one id lookup, one optional parent lookup, `d` ancestor
|
|
278
303
|
* lookups (the candidate parent's depth), one insert-or-update, one revision append.
|
|
279
304
|
*/
|
|
280
|
-
export declare function importTerm(required: ImportTermRequired,
|
|
305
|
+
export declare function importTerm(required: ImportTermRequired, optional?: ImportTermOptional): Promise<Term>;
|
|
281
306
|
export interface RenameTermRequired {
|
|
282
|
-
deps: WriteServiceDeps
|
|
307
|
+
deps: WriteServiceDeps & {
|
|
308
|
+
/** Required shared transaction. All repos, the watermark and outbox
|
|
309
|
+
* must share the store/connection opened by this port. */
|
|
310
|
+
transaction: TransactionalRepoPort["transaction"];
|
|
311
|
+
};
|
|
283
312
|
principalId: string;
|
|
284
313
|
termId: string;
|
|
285
314
|
newName: string;
|
|
286
315
|
}
|
|
287
|
-
/**
|
|
288
|
-
* watermark + outbox. `authorize
|
|
289
|
-
* side effects of any kind.
|
|
316
|
+
/** — same-tx rename + revision (carrying the pre-rename state) +
|
|
317
|
+
* watermark + outbox. `authorize` runs before the term lookup, so a denied caller produces zero
|
|
318
|
+
* side effects of any kind. A missing transaction capability fails closed before any lookup
|
|
319
|
+
* or write; a caller must supply a real shared transaction for durable atomicity.
|
|
320
|
+
* See docs/decisions/DR-005-ordered-taxonomy-validation.md.
|
|
321
|
+
*/
|
|
290
322
|
export declare function renameTerm(required: RenameTermRequired, _optional?: Record<string, never>): Promise<Term>;
|
|
291
323
|
export interface AssignTermsRequired {
|
|
292
324
|
deps: WriteServiceDeps;
|
|
@@ -315,14 +347,16 @@ export interface UnassignTermsRequired {
|
|
|
315
347
|
contentId: string;
|
|
316
348
|
termIds: string[];
|
|
317
349
|
}
|
|
318
|
-
/**
|
|
350
|
+
/** — spec debt this codebase never built (the spec
|
|
319
351
|
* always named "assign/unassign tools"; only `assignTerms` shipped). Same
|
|
320
352
|
* authorize -> validateAssignmentTarget -> write -> stamp -> outbox chokepoint as `assignTerms`,
|
|
321
|
-
* and — like `assignTerms` — never produces a `taxonomy_revisions` row (
|
|
353
|
+
* and — like `assignTerms` — never produces a `taxonomy_revisions` row ( disclosed
|
|
322
354
|
* narrowing applies equally to removal). Idempotent: unassigning a row that isn't there is a no-op,
|
|
323
|
-
* not an error (`remove
|
|
355
|
+
* not an error (`remove`'s 0-vs-1 return is intentionally not surfaced to the caller). Chosen over
|
|
324
356
|
* a "set terms" replace call — see the taxonomy plan's "What the docs settle" #3 for why replace
|
|
325
|
-
* risks silently erasing a concurrent assignment.
|
|
357
|
+
* risks silently erasing a concurrent assignment.
|
|
358
|
+
* See docs/decisions/DR-005-ordered-taxonomy-validation.md.
|
|
359
|
+
*/
|
|
326
360
|
export declare function unassignTerms(required: UnassignTermsRequired, _optional?: Record<string, never>): Promise<void>;
|
|
327
361
|
export interface EntryTermsCleanupPort {
|
|
328
362
|
deleteByContent(params: {
|
|
@@ -343,8 +377,18 @@ export interface OnContentDeletedRequired {
|
|
|
343
377
|
* `entry_terms` row that is inert on read; a periodic/boot reconciliation sweep — modeled by
|
|
344
378
|
* re-invoking this same function for the orphan — is the backstop, never a hard failure). A
|
|
345
379
|
* no-op for content with no
|
|
346
|
-
* assigned terms, never an error.
|
|
347
|
-
|
|
380
|
+
* assigned terms, never an error. Repository failures are warnings through the optional
|
|
381
|
+
* logger port; logger failure also cannot turn cleanup into a hard failure. Hosts should
|
|
382
|
+
* wire the logger for diagnostics; legacy callers without it remain silent. */
|
|
383
|
+
export declare function onContentDeleted(required: OnContentDeletedRequired, optional?: {
|
|
384
|
+
logger?: {
|
|
385
|
+
warn(required: {
|
|
386
|
+
message: string;
|
|
387
|
+
error: unknown;
|
|
388
|
+
event: OnContentDeletedRequired["event"];
|
|
389
|
+
}): void | Promise<void>;
|
|
390
|
+
};
|
|
391
|
+
}): Promise<void>;
|
|
348
392
|
/**
|
|
349
393
|
* Additive capabilities beyond the certified `TaxonomyRepoPort`/`TermRepoPort`/`EntryTermRepoPort`
|
|
350
394
|
* — same beyond-the-certified-port precedent `MergeableEntryTermRepoPort` established in this
|
|
@@ -354,10 +398,14 @@ export declare function onContentDeleted(required: OnContentDeletedRequired, _op
|
|
|
354
398
|
* SQLite adapter of its own — see this file's header and `repo.memory.ts`'s header for why).
|
|
355
399
|
*/
|
|
356
400
|
export interface DeletableTaxonomyRepoPort {
|
|
357
|
-
delete(id
|
|
401
|
+
delete({ id }: {
|
|
402
|
+
id: string;
|
|
403
|
+
}): Promise<void>;
|
|
358
404
|
}
|
|
359
405
|
export interface DeletableTermRepoPort {
|
|
360
|
-
delete(id
|
|
406
|
+
delete({ id }: {
|
|
407
|
+
id: string;
|
|
408
|
+
}): Promise<void>;
|
|
361
409
|
/** Direct-child count for `termId` as a parent. `deleteTerm` refuses to delete a term with
|
|
362
410
|
* children still pointing `parentId` at it — that reference would otherwise dangle the moment
|
|
363
411
|
* the parent row is gone, corrupting the hierarchy `validateHierarchyAssignment` polices on the
|
|
@@ -386,19 +434,28 @@ export interface AssignmentCountEntryTermRepoPort {
|
|
|
386
434
|
* assigned" instead of a generic failure. */
|
|
387
435
|
export declare class TermHasAssignedContentError extends Error {
|
|
388
436
|
readonly assignedCount: number;
|
|
389
|
-
constructor(
|
|
437
|
+
constructor(requiredArgs: {
|
|
438
|
+
message: string;
|
|
439
|
+
assignedCount: number;
|
|
440
|
+
}, _optionalArgs?: Record<string, never>);
|
|
390
441
|
}
|
|
391
442
|
/** See `DeletableTermRepoPort.countChildren`'s doc comment for why this guard exists. Carries
|
|
392
443
|
* `childCount` for the same reason `TermHasAssignedContentError` carries `assignedCount`. */
|
|
393
444
|
export declare class TermHasChildTermsError extends Error {
|
|
394
445
|
readonly childCount: number;
|
|
395
|
-
constructor(
|
|
446
|
+
constructor(requiredArgs: {
|
|
447
|
+
message: string;
|
|
448
|
+
childCount: number;
|
|
449
|
+
}, _optionalArgs?: Record<string, never>);
|
|
396
450
|
}
|
|
397
451
|
/** `deleteTaxonomy`'s content-assignment guard — `assignedCount` is the sum across every member
|
|
398
452
|
* term, not a single term's count. */
|
|
399
453
|
export declare class TaxonomyHasAssignedContentError extends Error {
|
|
400
454
|
readonly assignedCount: number;
|
|
401
|
-
constructor(
|
|
455
|
+
constructor(requiredArgs: {
|
|
456
|
+
message: string;
|
|
457
|
+
assignedCount: number;
|
|
458
|
+
}, _optionalArgs?: Record<string, never>);
|
|
402
459
|
}
|
|
403
460
|
/**
|
|
404
461
|
* Opens/commits/rolls back one transaction around `fn` — the same shape and contract as this
|
|
@@ -426,7 +483,9 @@ export declare class TaxonomyHasAssignedContentError extends Error {
|
|
|
426
483
|
* kind of backend they're running against.
|
|
427
484
|
*/
|
|
428
485
|
export interface TransactionalRepoPort {
|
|
429
|
-
transaction<T>(fn
|
|
486
|
+
transaction<T>({ fn }: {
|
|
487
|
+
fn: () => Promise<T>;
|
|
488
|
+
}): Promise<T>;
|
|
430
489
|
}
|
|
431
490
|
export interface DeleteTermRequired {
|
|
432
491
|
deps: WriteServiceDeps & {
|