@jini-ai/cms 0.3.10 → 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 +106 -45
- 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,24 +141,24 @@ 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;
|
|
148
151
|
revisions: TaxonomyRevisionRepoPort;
|
|
149
|
-
/** `core/gated-mutations.stampWatermark`-shaped, injected — same-transaction stamp per mutation.
|
|
150
|
-
|
|
152
|
+
/** `core/gated-mutations.stampWatermark`-shaped, injected — same-transaction stamp per mutation.
|
|
153
|
+
* May be sync (SQLite) or async (PGlite/Postgres); every call site awaits it, so an async stamp
|
|
154
|
+
* finishes, and its failure fails the write, before the mutation returns. */
|
|
155
|
+
stampWatermark: (required: Record<string, never>, optional?: {
|
|
156
|
+
tx?: unknown;
|
|
157
|
+
}) => Promise<void> | void;
|
|
151
158
|
outbox: {
|
|
152
|
-
enqueue: (event:
|
|
159
|
+
enqueue: ({ event }: {
|
|
160
|
+
event: unknown;
|
|
161
|
+
}) => Promise<void>;
|
|
153
162
|
};
|
|
154
163
|
/** The caller's own workspace — `validateContentJoin`'s `callerWorkspaceId` (Finding 1 fix). */
|
|
155
164
|
workspaceId: string;
|
|
@@ -162,14 +171,20 @@ export interface WriteServiceDeps {
|
|
|
162
171
|
contentTypeTaxonomyPolicy?: ContentTypeTaxonomyPolicyPort;
|
|
163
172
|
}
|
|
164
173
|
export declare class TaxonomyRecordNotFoundError extends Error {
|
|
165
|
-
constructor(
|
|
174
|
+
constructor(requiredArgs: {
|
|
175
|
+
message: string;
|
|
176
|
+
}, optionalArgs?: Record<string, never>);
|
|
166
177
|
}
|
|
167
178
|
export declare class TermRecordNotFoundError extends Error {
|
|
168
|
-
constructor(
|
|
179
|
+
constructor(requiredArgs: {
|
|
180
|
+
message: string;
|
|
181
|
+
}, optionalArgs?: Record<string, never>);
|
|
169
182
|
}
|
|
170
183
|
/** Finding 1 fix — the target of an `assignTerms` call does not resolve to any real content row. */
|
|
171
184
|
export declare class ContentRecordNotFoundError extends Error {
|
|
172
|
-
constructor(
|
|
185
|
+
constructor(requiredArgs: {
|
|
186
|
+
message: string;
|
|
187
|
+
}, optionalArgs?: Record<string, never>);
|
|
173
188
|
}
|
|
174
189
|
/**
|
|
175
190
|
* J1 (plan-publish-all-types-2026-09-25.md) — `importTaxonomy`/`importTerm`'s optimistic-concurrency
|
|
@@ -180,7 +195,9 @@ export declare class ContentRecordNotFoundError extends Error {
|
|
|
180
195
|
* "catch the one it actually called into" rule.
|
|
181
196
|
*/
|
|
182
197
|
export declare class TaxonomyVersionConflictError extends Error {
|
|
183
|
-
constructor(
|
|
198
|
+
constructor(requiredArgs: {
|
|
199
|
+
message: string;
|
|
200
|
+
}, optionalArgs?: Record<string, never>);
|
|
184
201
|
}
|
|
185
202
|
export interface CreateTaxonomyRequired {
|
|
186
203
|
deps: WriteServiceDeps;
|
|
@@ -196,13 +213,17 @@ export interface CreateTermRequired {
|
|
|
196
213
|
principalId: string;
|
|
197
214
|
taxonomyId: string;
|
|
198
215
|
name: string;
|
|
216
|
+
}
|
|
217
|
+
export interface CreateTermOptional {
|
|
199
218
|
parentId?: string | null | undefined;
|
|
200
219
|
}
|
|
201
|
-
/**
|
|
220
|
+
/** — validates `parentId` via `validateHierarchyAssignment` before writing. A
|
|
202
221
|
* freshly-created term has no descendants yet, so it structurally cannot be a cycle source; the
|
|
203
|
-
* cycle check is therefore always a no-op (`
|
|
204
|
-
* term (not yet built — no certified test in this slice exercises it).
|
|
205
|
-
|
|
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>;
|
|
206
227
|
/**
|
|
207
228
|
* Additive capability beyond the certified `TaxonomyRepoPort` — `findById`'s narrow return
|
|
208
229
|
* (`{id, hierarchical, allowList?}`) doesn't carry `version`/`name`/`status`/`updatedAt`, which
|
|
@@ -210,7 +231,9 @@ export declare function createTerm(required: CreateTermRequired, _optional?: Rec
|
|
|
210
231
|
* write an existing row back. Same beyond-the-certified-port precedent as `DeletableTaxonomyRepoPort`.
|
|
211
232
|
*/
|
|
212
233
|
export interface ImportableTaxonomyRepoPort {
|
|
213
|
-
findByIdFull(id
|
|
234
|
+
findByIdFull({ id }: {
|
|
235
|
+
id: string;
|
|
236
|
+
}): Promise<Taxonomy | null>;
|
|
214
237
|
update(row: Taxonomy): Promise<unknown>;
|
|
215
238
|
}
|
|
216
239
|
export interface ImportTaxonomyRequired {
|
|
@@ -247,7 +270,9 @@ export declare function importTaxonomy(required: ImportTaxonomyRequired, _option
|
|
|
247
270
|
* `TermRepoPort` already has `update`, unlike taxonomies, so no additive `update` is needed here.
|
|
248
271
|
*/
|
|
249
272
|
export interface ImportableTermRepoPort {
|
|
250
|
-
findByIdFull(id
|
|
273
|
+
findByIdFull({ id }: {
|
|
274
|
+
id: string;
|
|
275
|
+
}): Promise<Term | null>;
|
|
251
276
|
}
|
|
252
277
|
export interface ImportTermRequired {
|
|
253
278
|
deps: WriteServiceDeps & {
|
|
@@ -258,10 +283,12 @@ export interface ImportTermRequired {
|
|
|
258
283
|
id: string;
|
|
259
284
|
taxonomyId: string;
|
|
260
285
|
name: string;
|
|
261
|
-
parentId?: string | null | undefined;
|
|
262
286
|
/** Same three-way CAS contract as `importTaxonomy.expectedVersion`. */
|
|
263
287
|
expectedVersion: number | undefined;
|
|
264
288
|
}
|
|
289
|
+
export interface ImportTermOptional {
|
|
290
|
+
parentId?: string | null | undefined;
|
|
291
|
+
}
|
|
265
292
|
/**
|
|
266
293
|
* Slice J1 — the publish-content import chokepoint for `terms`. Address (name within
|
|
267
294
|
* `(taxonomyId, parentId)`) uniqueness is the publish factory's own precheck, not this function's
|
|
@@ -275,16 +302,23 @@ export interface ImportTermRequired {
|
|
|
275
302
|
* @complexity O(d) — one taxonomy lookup, one id lookup, one optional parent lookup, `d` ancestor
|
|
276
303
|
* lookups (the candidate parent's depth), one insert-or-update, one revision append.
|
|
277
304
|
*/
|
|
278
|
-
export declare function importTerm(required: ImportTermRequired,
|
|
305
|
+
export declare function importTerm(required: ImportTermRequired, optional?: ImportTermOptional): Promise<Term>;
|
|
279
306
|
export interface RenameTermRequired {
|
|
280
|
-
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
|
+
};
|
|
281
312
|
principalId: string;
|
|
282
313
|
termId: string;
|
|
283
314
|
newName: string;
|
|
284
315
|
}
|
|
285
|
-
/**
|
|
286
|
-
* watermark + outbox. `authorize
|
|
287
|
-
* 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
|
+
*/
|
|
288
322
|
export declare function renameTerm(required: RenameTermRequired, _optional?: Record<string, never>): Promise<Term>;
|
|
289
323
|
export interface AssignTermsRequired {
|
|
290
324
|
deps: WriteServiceDeps;
|
|
@@ -313,14 +347,16 @@ export interface UnassignTermsRequired {
|
|
|
313
347
|
contentId: string;
|
|
314
348
|
termIds: string[];
|
|
315
349
|
}
|
|
316
|
-
/**
|
|
350
|
+
/** — spec debt this codebase never built (the spec
|
|
317
351
|
* always named "assign/unassign tools"; only `assignTerms` shipped). Same
|
|
318
352
|
* authorize -> validateAssignmentTarget -> write -> stamp -> outbox chokepoint as `assignTerms`,
|
|
319
|
-
* and — like `assignTerms` — never produces a `taxonomy_revisions` row (
|
|
353
|
+
* and — like `assignTerms` — never produces a `taxonomy_revisions` row ( disclosed
|
|
320
354
|
* narrowing applies equally to removal). Idempotent: unassigning a row that isn't there is a no-op,
|
|
321
|
-
* not an error (`remove
|
|
355
|
+
* not an error (`remove`'s 0-vs-1 return is intentionally not surfaced to the caller). Chosen over
|
|
322
356
|
* a "set terms" replace call — see the taxonomy plan's "What the docs settle" #3 for why replace
|
|
323
|
-
* risks silently erasing a concurrent assignment.
|
|
357
|
+
* risks silently erasing a concurrent assignment.
|
|
358
|
+
* See docs/decisions/DR-005-ordered-taxonomy-validation.md.
|
|
359
|
+
*/
|
|
324
360
|
export declare function unassignTerms(required: UnassignTermsRequired, _optional?: Record<string, never>): Promise<void>;
|
|
325
361
|
export interface EntryTermsCleanupPort {
|
|
326
362
|
deleteByContent(params: {
|
|
@@ -341,8 +377,18 @@ export interface OnContentDeletedRequired {
|
|
|
341
377
|
* `entry_terms` row that is inert on read; a periodic/boot reconciliation sweep — modeled by
|
|
342
378
|
* re-invoking this same function for the orphan — is the backstop, never a hard failure). A
|
|
343
379
|
* no-op for content with no
|
|
344
|
-
* assigned terms, never an error.
|
|
345
|
-
|
|
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>;
|
|
346
392
|
/**
|
|
347
393
|
* Additive capabilities beyond the certified `TaxonomyRepoPort`/`TermRepoPort`/`EntryTermRepoPort`
|
|
348
394
|
* — same beyond-the-certified-port precedent `MergeableEntryTermRepoPort` established in this
|
|
@@ -352,10 +398,14 @@ export declare function onContentDeleted(required: OnContentDeletedRequired, _op
|
|
|
352
398
|
* SQLite adapter of its own — see this file's header and `repo.memory.ts`'s header for why).
|
|
353
399
|
*/
|
|
354
400
|
export interface DeletableTaxonomyRepoPort {
|
|
355
|
-
delete(id
|
|
401
|
+
delete({ id }: {
|
|
402
|
+
id: string;
|
|
403
|
+
}): Promise<void>;
|
|
356
404
|
}
|
|
357
405
|
export interface DeletableTermRepoPort {
|
|
358
|
-
delete(id
|
|
406
|
+
delete({ id }: {
|
|
407
|
+
id: string;
|
|
408
|
+
}): Promise<void>;
|
|
359
409
|
/** Direct-child count for `termId` as a parent. `deleteTerm` refuses to delete a term with
|
|
360
410
|
* children still pointing `parentId` at it — that reference would otherwise dangle the moment
|
|
361
411
|
* the parent row is gone, corrupting the hierarchy `validateHierarchyAssignment` polices on the
|
|
@@ -384,19 +434,28 @@ export interface AssignmentCountEntryTermRepoPort {
|
|
|
384
434
|
* assigned" instead of a generic failure. */
|
|
385
435
|
export declare class TermHasAssignedContentError extends Error {
|
|
386
436
|
readonly assignedCount: number;
|
|
387
|
-
constructor(
|
|
437
|
+
constructor(requiredArgs: {
|
|
438
|
+
message: string;
|
|
439
|
+
assignedCount: number;
|
|
440
|
+
}, _optionalArgs?: Record<string, never>);
|
|
388
441
|
}
|
|
389
442
|
/** See `DeletableTermRepoPort.countChildren`'s doc comment for why this guard exists. Carries
|
|
390
443
|
* `childCount` for the same reason `TermHasAssignedContentError` carries `assignedCount`. */
|
|
391
444
|
export declare class TermHasChildTermsError extends Error {
|
|
392
445
|
readonly childCount: number;
|
|
393
|
-
constructor(
|
|
446
|
+
constructor(requiredArgs: {
|
|
447
|
+
message: string;
|
|
448
|
+
childCount: number;
|
|
449
|
+
}, _optionalArgs?: Record<string, never>);
|
|
394
450
|
}
|
|
395
451
|
/** `deleteTaxonomy`'s content-assignment guard — `assignedCount` is the sum across every member
|
|
396
452
|
* term, not a single term's count. */
|
|
397
453
|
export declare class TaxonomyHasAssignedContentError extends Error {
|
|
398
454
|
readonly assignedCount: number;
|
|
399
|
-
constructor(
|
|
455
|
+
constructor(requiredArgs: {
|
|
456
|
+
message: string;
|
|
457
|
+
assignedCount: number;
|
|
458
|
+
}, _optionalArgs?: Record<string, never>);
|
|
400
459
|
}
|
|
401
460
|
/**
|
|
402
461
|
* Opens/commits/rolls back one transaction around `fn` — the same shape and contract as this
|
|
@@ -424,7 +483,9 @@ export declare class TaxonomyHasAssignedContentError extends Error {
|
|
|
424
483
|
* kind of backend they're running against.
|
|
425
484
|
*/
|
|
426
485
|
export interface TransactionalRepoPort {
|
|
427
|
-
transaction<T>(fn
|
|
486
|
+
transaction<T>({ fn }: {
|
|
487
|
+
fn: () => Promise<T>;
|
|
488
|
+
}): Promise<T>;
|
|
428
489
|
}
|
|
429
490
|
export interface DeleteTermRequired {
|
|
430
491
|
deps: WriteServiceDeps & {
|