@jini-ai/cms 0.3.11 → 0.4.1
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 +27 -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/core/tools/registration-kit.d.ts +0 -346
- package/dist/core/tools/registration-kit.js +0 -426
- 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/server/index.d.ts +0 -28
- package/dist/server/index.js +0 -28
- /package/dist/{identity → media/import}/ports.js +0 -0
|
@@ -1,111 +1,117 @@
|
|
|
1
|
+
import { nowIso as kernelNowIso } from "@jini-ai/core/primitives";
|
|
1
2
|
import { ContentTypeLifecycleError, ContentTypeNotFoundError, ForbiddenError, VersionConflictError } from "./errors.js";
|
|
2
3
|
async function resolveActiveOrDeprecated(repo, workspaceId, key) {
|
|
3
4
|
const current = await repo.findByKey({ workspaceId, key });
|
|
4
5
|
if (!current) {
|
|
5
|
-
return { ok: false, error: new ContentTypeNotFoundError(`content type '${key}' was not found in workspace '${workspaceId}'`) };
|
|
6
|
+
return { ok: false, error: new ContentTypeNotFoundError({ message: `content type '${key}' was not found in workspace '${workspaceId}'` }) };
|
|
6
7
|
}
|
|
7
8
|
return { ok: true, value: current };
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
* enqueues `content_type.deprecated` in the same call. Rejected unconditionally
|
|
11
|
+
* — flips an active type to `deprecated` (blocks only NEW entry creation) and
|
|
12
|
+
* enqueues `content_type.deprecated` in the same call. Rejected unconditionally if the
|
|
12
13
|
* type is already `tombstone` — deprecate can never be used to "step back" out of tombstone.
|
|
13
14
|
*
|
|
14
15
|
* @complexity O(1) plus one repo read, one same-tx write pair, and one outbox enqueue.
|
|
15
16
|
* @overallScore 100
|
|
17
|
+
* See docs/decisions/DR-002-content-lifecycle-and-cleanup.md.
|
|
16
18
|
*/
|
|
17
19
|
export async function deprecateContentType(required) {
|
|
18
20
|
const { deps, input } = required;
|
|
19
|
-
const authResult = await deps.authorize({ principalId: input.actorId, permission: "admin.collections.manage", workspaceId: input.workspaceId, entityType: "content-type" });
|
|
21
|
+
const authResult = await deps.authorize({ principalId: input.actorId, permission: "admin.collections.manage", workspaceId: input.workspaceId }, { entityType: "content-type" });
|
|
20
22
|
if (!authResult.allowed) {
|
|
21
|
-
return { ok: false, error: new ForbiddenError(`principal '${input.actorId}' cannot deprecate content type '${input.key}' (${authResult.reason})`) };
|
|
23
|
+
return { ok: false, error: new ForbiddenError({ message: `principal '${input.actorId}' cannot deprecate content type '${input.key}' (${authResult.reason})` }) };
|
|
22
24
|
}
|
|
23
25
|
const resolved = await resolveActiveOrDeprecated(deps.repo, input.workspaceId, input.key);
|
|
24
26
|
if (!resolved.ok)
|
|
25
27
|
return resolved;
|
|
26
28
|
const current = resolved.value;
|
|
27
29
|
if (current.status === "tombstone") {
|
|
28
|
-
|
|
30
|
+
// See docs/decisions/DR-002-content-lifecycle-and-cleanup.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
|
|
31
|
+
return { ok: false, error: new ContentTypeLifecycleError({ message: `content type '${input.key}' is tombstoned; no transition out of tombstone is allowed` }) };
|
|
29
32
|
}
|
|
30
33
|
if (input.expectedVersion !== current.version) {
|
|
31
|
-
return { ok: false, error: new VersionConflictError(`expected version ${input.expectedVersion} for content type '${input.key}', found ${current.version}`) };
|
|
34
|
+
return { ok: false, error: new VersionConflictError({ message: `expected version ${input.expectedVersion} for content type '${input.key}', found ${current.version}` }) };
|
|
32
35
|
}
|
|
33
|
-
const now = deps.clock
|
|
36
|
+
const now = kernelNowIso({ clock: deps.clock });
|
|
34
37
|
const updated = { ...current, status: "deprecated", version: current.version + 1 };
|
|
35
|
-
await deps.repo.transaction(async () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
38
|
+
await deps.repo.transaction({ fn: async () => {
|
|
39
|
+
await deps.repo.save(updated);
|
|
40
|
+
await deps.repo.appendRevision({
|
|
41
|
+
contentTypeKey: input.key,
|
|
42
|
+
workspaceId: input.workspaceId,
|
|
43
|
+
op: "field-change",
|
|
44
|
+
stateJson: updated,
|
|
45
|
+
actorId: input.actorId,
|
|
46
|
+
principalKind: input.principalKind ?? null,
|
|
47
|
+
delegatedByWorkspaceId: null,
|
|
48
|
+
delegatedById: null,
|
|
49
|
+
recordedAt: now,
|
|
50
|
+
});
|
|
51
|
+
} });
|
|
49
52
|
await deps.outbox.enqueue({ name: "content_type.deprecated", payload: { workspaceId: input.workspaceId, key: input.key } });
|
|
50
53
|
return { ok: true, value: { contentType: updated } };
|
|
51
54
|
}
|
|
52
55
|
/**
|
|
53
|
-
*
|
|
54
|
-
* `active`. Rejected unconditionally
|
|
56
|
+
* — flips a `deprecated` (or already-`active`, a no-op-shaped success) type back to
|
|
57
|
+
* `active`. Rejected unconditionally against a tombstoned row.
|
|
55
58
|
*
|
|
56
59
|
* @complexity O(1) plus one repo read and one same-tx write pair.
|
|
57
60
|
* @overallScore 100
|
|
61
|
+
* See docs/decisions/DR-002-content-lifecycle-and-cleanup.md.
|
|
58
62
|
*/
|
|
59
63
|
export async function reactivateContentType(required) {
|
|
60
64
|
const { deps, input } = required;
|
|
61
|
-
const authResult = await deps.authorize({ principalId: input.actorId, permission: "admin.collections.manage", workspaceId: input.workspaceId, entityType: "content-type" });
|
|
65
|
+
const authResult = await deps.authorize({ principalId: input.actorId, permission: "admin.collections.manage", workspaceId: input.workspaceId }, { entityType: "content-type" });
|
|
62
66
|
if (!authResult.allowed) {
|
|
63
|
-
return { ok: false, error: new ForbiddenError(`principal '${input.actorId}' cannot reactivate content type '${input.key}' (${authResult.reason})`) };
|
|
67
|
+
return { ok: false, error: new ForbiddenError({ message: `principal '${input.actorId}' cannot reactivate content type '${input.key}' (${authResult.reason})` }) };
|
|
64
68
|
}
|
|
65
69
|
const resolved = await resolveActiveOrDeprecated(deps.repo, input.workspaceId, input.key);
|
|
66
70
|
if (!resolved.ok)
|
|
67
71
|
return resolved;
|
|
68
72
|
const current = resolved.value;
|
|
69
73
|
if (current.status === "tombstone") {
|
|
70
|
-
|
|
74
|
+
// See docs/decisions/DR-002-content-lifecycle-and-cleanup.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
|
|
75
|
+
return { ok: false, error: new ContentTypeLifecycleError({ message: `content type '${input.key}' is tombstoned; no transition out of tombstone is allowed` }) };
|
|
71
76
|
}
|
|
72
77
|
if (input.expectedVersion !== current.version) {
|
|
73
|
-
return { ok: false, error: new VersionConflictError(`expected version ${input.expectedVersion} for content type '${input.key}', found ${current.version}`) };
|
|
78
|
+
return { ok: false, error: new VersionConflictError({ message: `expected version ${input.expectedVersion} for content type '${input.key}', found ${current.version}` }) };
|
|
74
79
|
}
|
|
75
|
-
const now = deps.clock
|
|
80
|
+
const now = kernelNowIso({ clock: deps.clock });
|
|
76
81
|
const updated = { ...current, status: "active", version: current.version + 1 };
|
|
77
|
-
await deps.repo.transaction(async () => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
await deps.repo.transaction({ fn: async () => {
|
|
83
|
+
await deps.repo.save(updated);
|
|
84
|
+
await deps.repo.appendRevision({
|
|
85
|
+
contentTypeKey: input.key,
|
|
86
|
+
workspaceId: input.workspaceId,
|
|
87
|
+
op: "field-change",
|
|
88
|
+
stateJson: updated,
|
|
89
|
+
actorId: input.actorId,
|
|
90
|
+
principalKind: input.principalKind ?? null,
|
|
91
|
+
delegatedByWorkspaceId: null,
|
|
92
|
+
delegatedById: null,
|
|
93
|
+
recordedAt: now,
|
|
94
|
+
});
|
|
95
|
+
} });
|
|
91
96
|
return { ok: true, value: { contentType: updated } };
|
|
92
97
|
}
|
|
93
98
|
/**
|
|
94
|
-
*
|
|
99
|
+
* — the one-way terminal transition into `tombstone`. Must be entered from
|
|
95
100
|
* `deprecated` (any other status, including an already-tombstoned row, is rejected with the same
|
|
96
|
-
* "must deprecate first" guard —
|
|
101
|
+
* "must deprecate first" guard — collapse to one check). Tears down every queryable-
|
|
97
102
|
* field index the type ever provisioned BEFORE persisting the status flip, and enqueues
|
|
98
103
|
* `content_type.tombstoned` in the same call.
|
|
99
104
|
*
|
|
100
105
|
* @complexity O(1) plus one repo read, one index-teardown call, one same-tx write pair, and one
|
|
101
106
|
* outbox enqueue.
|
|
102
107
|
* @overallScore 100
|
|
108
|
+
* See docs/decisions/DR-002-content-lifecycle-and-cleanup.md.
|
|
103
109
|
*/
|
|
104
110
|
export async function tombstoneContentType(required) {
|
|
105
111
|
const { deps, input } = required;
|
|
106
|
-
const authResult = await deps.authorize({ principalId: input.actorId, permission: "admin.collections.manage", workspaceId: input.workspaceId, entityType: "content-type" });
|
|
112
|
+
const authResult = await deps.authorize({ principalId: input.actorId, permission: "admin.collections.manage", workspaceId: input.workspaceId }, { entityType: "content-type" });
|
|
107
113
|
if (!authResult.allowed) {
|
|
108
|
-
return { ok: false, error: new ForbiddenError(`principal '${input.actorId}' cannot tombstone content type '${input.key}' (${authResult.reason})`) };
|
|
114
|
+
return { ok: false, error: new ForbiddenError({ message: `principal '${input.actorId}' cannot tombstone content type '${input.key}' (${authResult.reason})` }) };
|
|
109
115
|
}
|
|
110
116
|
const resolved = await resolveActiveOrDeprecated(deps.repo, input.workspaceId, input.key);
|
|
111
117
|
if (!resolved.ok)
|
|
@@ -114,29 +120,30 @@ export async function tombstoneContentType(required) {
|
|
|
114
120
|
if (current.status !== "deprecated") {
|
|
115
121
|
return {
|
|
116
122
|
ok: false,
|
|
117
|
-
|
|
123
|
+
// See docs/decisions/DR-002-content-lifecycle-and-cleanup.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
|
|
124
|
+
error: new ContentTypeLifecycleError({ message: `content type '${input.key}' must be 'deprecated' before it can be tombstoned; current status is '${current.status}'` }),
|
|
118
125
|
};
|
|
119
126
|
}
|
|
120
127
|
if (input.expectedVersion !== current.version) {
|
|
121
|
-
return { ok: false, error: new VersionConflictError(`expected version ${input.expectedVersion} for content type '${input.key}', found ${current.version}`) };
|
|
128
|
+
return { ok: false, error: new VersionConflictError({ message: `expected version ${input.expectedVersion} for content type '${input.key}', found ${current.version}` }) };
|
|
122
129
|
}
|
|
123
|
-
const now = deps.clock
|
|
130
|
+
const now = kernelNowIso({ clock: deps.clock });
|
|
124
131
|
const updated = { ...current, status: "tombstone", tombstonedAt: now, version: current.version + 1 };
|
|
125
132
|
await deps.indexProvisioner.tearDownAllIndexesForContentType({ workspaceId: input.workspaceId, contentTypeKey: input.key });
|
|
126
|
-
await deps.repo.transaction(async () => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
await deps.repo.transaction({ fn: async () => {
|
|
134
|
+
await deps.repo.save(updated);
|
|
135
|
+
await deps.repo.appendRevision({
|
|
136
|
+
contentTypeKey: input.key,
|
|
137
|
+
workspaceId: input.workspaceId,
|
|
138
|
+
op: "field-change",
|
|
139
|
+
stateJson: updated,
|
|
140
|
+
actorId: input.actorId,
|
|
141
|
+
principalKind: input.principalKind ?? null,
|
|
142
|
+
delegatedByWorkspaceId: null,
|
|
143
|
+
delegatedById: null,
|
|
144
|
+
recordedAt: now,
|
|
145
|
+
});
|
|
146
|
+
} });
|
|
140
147
|
await deps.outbox.enqueue({ name: "content_type.tombstoned", payload: { workspaceId: input.workspaceId, key: input.key } });
|
|
141
148
|
return { ok: true, value: { contentType: updated } };
|
|
142
149
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Clock } from "@jini-ai/core/primitives";
|
|
1
2
|
import type { ContentTypeRepoPort, ContentTypeRevisionInput, IndexProvisionerPort, OutboxPort } from "./write-service.js";
|
|
2
3
|
import type { TeardownIndexProvisionerPort } from "./lifecycle.js";
|
|
3
4
|
import type { ContentTypeListPort } from "./list.js";
|
|
@@ -32,7 +33,9 @@ export declare class InMemoryContentTypeRepo implements ContentTypeRepoPort, Con
|
|
|
32
33
|
workspaceId: string;
|
|
33
34
|
key: string;
|
|
34
35
|
}): Promise<ContentTypeRecord | null>;
|
|
35
|
-
transaction<T>(fn
|
|
36
|
+
transaction<T>({ fn }: {
|
|
37
|
+
fn: () => Promise<T>;
|
|
38
|
+
}): Promise<T>;
|
|
36
39
|
listByWorkspace(params: {
|
|
37
40
|
workspaceId: string;
|
|
38
41
|
}): Promise<ContentTypeRecord[]>;
|
|
@@ -59,9 +62,7 @@ export declare function toContentTypeOutbox(deps: {
|
|
|
59
62
|
payload: Record<string, unknown>;
|
|
60
63
|
}): Promise<void>;
|
|
61
64
|
};
|
|
62
|
-
clock:
|
|
63
|
-
nowIso(): string;
|
|
64
|
-
};
|
|
65
|
+
clock: Clock;
|
|
65
66
|
idGen: {
|
|
66
67
|
newId(): string;
|
|
67
68
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nowIso as kernelNowIso } from "@jini-ai/core/primitives";
|
|
1
2
|
/**
|
|
2
3
|
* @file In-memory adapters for the `content-types` package's write/list ports (port/adapter rule-of-two
|
|
3
4
|
* "one being built now" half). Backs `server/app.ts`'s hermetic test/dev composition AND, until a
|
|
@@ -36,7 +37,7 @@ export class InMemoryContentTypeRepo {
|
|
|
36
37
|
const row = this.rows.get(InMemoryContentTypeRepo.key(params.workspaceId, params.key));
|
|
37
38
|
return row ? { ...row } : null;
|
|
38
39
|
}
|
|
39
|
-
async transaction(fn) {
|
|
40
|
+
async transaction({ fn }) {
|
|
40
41
|
return fn();
|
|
41
42
|
}
|
|
42
43
|
async listByWorkspace(params) {
|
|
@@ -62,7 +63,7 @@ export function toContentTypeOutbox(deps) {
|
|
|
62
63
|
id: deps.idGen.newId(),
|
|
63
64
|
workspaceId: deps.workspaceId,
|
|
64
65
|
name: event.name,
|
|
65
|
-
occurredAt: deps.clock
|
|
66
|
+
occurredAt: kernelNowIso({ clock: deps.clock }),
|
|
66
67
|
payload: event.payload,
|
|
67
68
|
});
|
|
68
69
|
},
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { IdGenerator } from "@jini-ai/core/primitives";
|
|
2
|
+
import type { Clock } from "@jini-ai/core/primitives";
|
|
1
3
|
/**
|
|
2
4
|
* @file Content-types' half of the agent-tool authorization wiring: maps `agent-tools.ts`'s catalog entries onto the
|
|
3
5
|
* `write-service.ts`/`lifecycle.ts` functions the admin HTTP routes call, as `ToolRegistration`s.
|
|
@@ -32,7 +34,7 @@
|
|
|
32
34
|
*/
|
|
33
35
|
import type { AuthorizeFn } from "../core/commands/command.js";
|
|
34
36
|
import type { OutboxPort } from "../core/ports.js";
|
|
35
|
-
import { type DerivedRiskByToolId, type ToolRegistration } from "
|
|
37
|
+
import { type DerivedRiskByToolId, type ToolRegistration } from "@jini-ai/core";
|
|
36
38
|
import { type ContentTypeListPort } from "./list.js";
|
|
37
39
|
import { type TeardownIndexProvisionerPort } from "./lifecycle.js";
|
|
38
40
|
import { type ContentTypeRepoPort, type IndexProvisionerPort } from "./write-service.js";
|
|
@@ -40,17 +42,13 @@ import { type ContentTypeRepoPort, type IndexProvisionerPort } from "./write-ser
|
|
|
40
42
|
* The exact slice of the route-deps bag Content-Types' tool handlers read. Declared structurally
|
|
41
43
|
* (rather than importing `server/routes/types`'s `RouteDeps`) so this module carries no back-edge
|
|
42
44
|
* into the composition root. `server/routes/*` satisfies this structurally by passing its existing
|
|
43
|
-
* `RouteDeps` object
|
|
45
|
+
* `RouteDeps` object with the kernel clock/ID contracts.
|
|
44
46
|
*/
|
|
45
47
|
export interface ContentTypesToolDeps {
|
|
46
48
|
authorize: AuthorizeFn;
|
|
47
49
|
workspaceId: string;
|
|
48
|
-
clock:
|
|
49
|
-
|
|
50
|
-
};
|
|
51
|
-
idGen: {
|
|
52
|
-
newId(): string;
|
|
53
|
-
};
|
|
50
|
+
clock: Clock;
|
|
51
|
+
idGen: IdGenerator;
|
|
54
52
|
outbox: OutboxPort;
|
|
55
53
|
contentTypeRepo: ContentTypeRepoPort & ContentTypeListPort;
|
|
56
54
|
contentTypeIndexProvisioner: IndexProvisionerPort & TeardownIndexProvisionerPort;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { AGENT_TOOL_PRINCIPAL_KIND, buildDomainRegistrations, decorateWithSchema, fromResult, indexCatalogById, requireInputRecord, requireNoInput, requireNumber, requireString
|
|
1
|
+
import { AGENT_TOOL_PRINCIPAL_KIND, buildDomainRegistrations, decorateWithSchema, fromResult, indexCatalogById, requireInputRecord, requireNoInput, requireNumber, requireString } from "@jini-ai/core";
|
|
2
|
+
import { adaptLegacyAuthorize, requireToolPermission } from "../core/tools/index.js";
|
|
2
3
|
import { contentTypesAgentToolCatalog } from "./agent-tools.js";
|
|
3
4
|
import { parseContentTypeFieldDefs } from "./field-defs.js";
|
|
4
5
|
import { listContentTypes } from "./list.js";
|
|
5
6
|
import { deprecateContentType, reactivateContentType, tombstoneContentType } from "./lifecycle.js";
|
|
6
7
|
import { registerContentType, updateContentTypeFields } from "./write-service.js";
|
|
7
|
-
const CATALOG_BY_ID = indexCatalogById(contentTypesAgentToolCatalog);
|
|
8
|
+
const CATALOG_BY_ID = indexCatalogById({ catalog: contentTypesAgentToolCatalog });
|
|
8
9
|
/** Descriptors for the 2 cleanup tools this pass does not wire — see file header. */
|
|
9
10
|
const UNWIRED_CONTENT_TYPES_TOOL_IDS = new Set(["collections_plan_cleanup", "collections_execute_cleanup"]);
|
|
10
11
|
/**
|
|
@@ -39,7 +40,7 @@ export const contentTypesDerivedRisk = new Map([
|
|
|
39
40
|
* @overallScore 100
|
|
40
41
|
*/
|
|
41
42
|
function requireFields(input, toolId) {
|
|
42
|
-
const parsed = parseContentTypeFieldDefs(input.fields);
|
|
43
|
+
const parsed = parseContentTypeFieldDefs({ value: input.fields });
|
|
43
44
|
if (parsed.ok)
|
|
44
45
|
return parsed.value;
|
|
45
46
|
throw decorateWithSchema({ toolId, catalog: CATALOG_BY_ID, message: parsed.error.message });
|
|
@@ -76,14 +77,14 @@ function toContentTypeView(record) {
|
|
|
76
77
|
return view;
|
|
77
78
|
}
|
|
78
79
|
function fromContentTypeResult(fn) {
|
|
79
|
-
return fromResult(fn).then(({ contentType }) => ({ contentType: toContentTypeView(contentType) }));
|
|
80
|
+
return fromResult({ fn: fn }).then(({ contentType }) => ({ contentType: toContentTypeView(contentType) }));
|
|
80
81
|
}
|
|
81
82
|
function contentTypesDeps(routeDeps) {
|
|
82
83
|
return {
|
|
83
84
|
repo: routeDeps.contentTypeRepo,
|
|
84
85
|
clock: routeDeps.clock,
|
|
85
86
|
ids: routeDeps.idGen,
|
|
86
|
-
authorize: routeDeps.authorize,
|
|
87
|
+
authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }),
|
|
87
88
|
outbox: routeDeps.outbox,
|
|
88
89
|
indexProvisioner: routeDeps.contentTypeIndexProvisioner,
|
|
89
90
|
};
|
|
@@ -91,79 +92,75 @@ function contentTypesDeps(routeDeps) {
|
|
|
91
92
|
export function buildContentTypesRegistrations(routeDeps) {
|
|
92
93
|
const handlers = {
|
|
93
94
|
collections_content_type_list: async (ctx) => {
|
|
94
|
-
requireNoInput(ctx.input);
|
|
95
|
-
await requireToolPermission(routeDeps, {
|
|
96
|
-
principalId: ctx.principal.id,
|
|
97
|
-
permission: "admin.collections.read",
|
|
98
|
-
entityType: "content-type",
|
|
99
|
-
});
|
|
95
|
+
requireNoInput({ input: ctx.input });
|
|
96
|
+
await requireToolPermission({ authorize: adaptLegacyAuthorize({ authorize: routeDeps.authorize }), workspaceId: routeDeps.workspaceId, principalId: ctx.principal.id, permission: "admin.collections.read" }, { entityType: "content-type" });
|
|
100
97
|
const { items } = await listContentTypes({ repo: routeDeps.contentTypeRepo, workspaceId: routeDeps.workspaceId });
|
|
101
98
|
return { contentTypes: items.map(toContentTypeView) };
|
|
102
99
|
},
|
|
103
100
|
collections_content_type_define: async (ctx) => {
|
|
104
|
-
const input = requireInputRecord(ctx.input);
|
|
101
|
+
const input = requireInputRecord({ input: ctx.input });
|
|
105
102
|
return fromContentTypeResult(() => registerContentType({
|
|
106
103
|
deps: contentTypesDeps(routeDeps),
|
|
107
104
|
input: {
|
|
108
105
|
actorId: ctx.principal.id,
|
|
109
106
|
principalKind: AGENT_TOOL_PRINCIPAL_KIND,
|
|
110
107
|
workspaceId: routeDeps.workspaceId,
|
|
111
|
-
key: requireString(input, "key"),
|
|
112
|
-
label: requireString(input, "label"),
|
|
108
|
+
key: requireString({ input, key: "key" }),
|
|
109
|
+
label: requireString({ input, key: "label" }),
|
|
113
110
|
fields: requireFields(input, "collections_content_type_define"),
|
|
114
111
|
},
|
|
115
112
|
}));
|
|
116
113
|
},
|
|
117
114
|
collections_content_type_update_fields: async (ctx) => {
|
|
118
|
-
const input = requireInputRecord(ctx.input);
|
|
115
|
+
const input = requireInputRecord({ input: ctx.input });
|
|
119
116
|
return fromContentTypeResult(() => updateContentTypeFields({
|
|
120
117
|
deps: contentTypesDeps(routeDeps),
|
|
121
118
|
input: {
|
|
122
119
|
actorId: ctx.principal.id,
|
|
123
120
|
principalKind: AGENT_TOOL_PRINCIPAL_KIND,
|
|
124
121
|
workspaceId: routeDeps.workspaceId,
|
|
125
|
-
key: requireString(input, "key"),
|
|
122
|
+
key: requireString({ input, key: "key" }),
|
|
126
123
|
fields: requireFields(input, "collections_content_type_update_fields"),
|
|
127
|
-
expectedVersion: requireNumber(input, "expectedVersion"),
|
|
124
|
+
expectedVersion: requireNumber({ input, key: "expectedVersion" }),
|
|
128
125
|
},
|
|
129
126
|
}));
|
|
130
127
|
},
|
|
131
128
|
collections_content_type_deprecate: async (ctx) => {
|
|
132
|
-
const input = requireInputRecord(ctx.input);
|
|
129
|
+
const input = requireInputRecord({ input: ctx.input });
|
|
133
130
|
return fromContentTypeResult(() => deprecateContentType({
|
|
134
131
|
deps: contentTypesDeps(routeDeps),
|
|
135
132
|
input: {
|
|
136
133
|
actorId: ctx.principal.id,
|
|
137
134
|
principalKind: AGENT_TOOL_PRINCIPAL_KIND,
|
|
138
135
|
workspaceId: routeDeps.workspaceId,
|
|
139
|
-
key: requireString(input, "key"),
|
|
140
|
-
expectedVersion: requireNumber(input, "expectedVersion"),
|
|
136
|
+
key: requireString({ input, key: "key" }),
|
|
137
|
+
expectedVersion: requireNumber({ input, key: "expectedVersion" }),
|
|
141
138
|
},
|
|
142
139
|
}));
|
|
143
140
|
},
|
|
144
141
|
collections_content_type_reactivate: async (ctx) => {
|
|
145
|
-
const input = requireInputRecord(ctx.input);
|
|
142
|
+
const input = requireInputRecord({ input: ctx.input });
|
|
146
143
|
return fromContentTypeResult(() => reactivateContentType({
|
|
147
144
|
deps: contentTypesDeps(routeDeps),
|
|
148
145
|
input: {
|
|
149
146
|
actorId: ctx.principal.id,
|
|
150
147
|
principalKind: AGENT_TOOL_PRINCIPAL_KIND,
|
|
151
148
|
workspaceId: routeDeps.workspaceId,
|
|
152
|
-
key: requireString(input, "key"),
|
|
153
|
-
expectedVersion: requireNumber(input, "expectedVersion"),
|
|
149
|
+
key: requireString({ input, key: "key" }),
|
|
150
|
+
expectedVersion: requireNumber({ input, key: "expectedVersion" }),
|
|
154
151
|
},
|
|
155
152
|
}));
|
|
156
153
|
},
|
|
157
154
|
collections_content_type_tombstone: async (ctx) => {
|
|
158
|
-
const input = requireInputRecord(ctx.input);
|
|
155
|
+
const input = requireInputRecord({ input: ctx.input });
|
|
159
156
|
return fromContentTypeResult(() => tombstoneContentType({
|
|
160
157
|
deps: contentTypesDeps(routeDeps),
|
|
161
158
|
input: {
|
|
162
159
|
actorId: ctx.principal.id,
|
|
163
160
|
principalKind: AGENT_TOOL_PRINCIPAL_KIND,
|
|
164
161
|
workspaceId: routeDeps.workspaceId,
|
|
165
|
-
key: requireString(input, "key"),
|
|
166
|
-
expectedVersion: requireNumber(input, "expectedVersion"),
|
|
162
|
+
key: requireString({ input, key: "key" }),
|
|
163
|
+
expectedVersion: requireNumber({ input, key: "expectedVersion" }),
|
|
167
164
|
},
|
|
168
165
|
}));
|
|
169
166
|
},
|
|
@@ -174,7 +171,6 @@ export function buildContentTypesRegistrations(routeDeps) {
|
|
|
174
171
|
catalog: CATALOG_BY_ID,
|
|
175
172
|
handlers,
|
|
176
173
|
derivedRisk: contentTypesDerivedRisk,
|
|
177
|
-
|
|
178
|
-
});
|
|
174
|
+
}, { unwiredToolIds: UNWIRED_CONTENT_TYPES_TOOL_IDS });
|
|
179
175
|
}
|
|
180
176
|
//# sourceMappingURL=tool-registrations.js.map
|
|
@@ -24,15 +24,16 @@
|
|
|
24
24
|
* `features/content-types` domain vocabulary. No dependencies.
|
|
25
25
|
*/
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* — the closed, 5-entry INDEXABLE SCALAR set. Never extend ad hoc.
|
|
28
28
|
*
|
|
29
29
|
* Unchanged since the original enum: every value that has ever reached a `CAST` literal is still
|
|
30
30
|
* here, in this order. Kept as its own constant so the "these five are the original, untouched
|
|
31
31
|
* scalars" claim is expressible in code rather than only in a comment.
|
|
32
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
32
33
|
*/
|
|
33
34
|
export declare const CONTENT_TYPE_SCALAR_KINDS: readonly ["text", "integer", "real", "boolean", "datetime"];
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* — kinds that MAY carry `queryable: true` and therefore MAY reach
|
|
36
37
|
* `mapFieldKindToCast`. Every member MUST have an entry in `index-provisioning.ts`'s
|
|
37
38
|
* `KIND_TO_CAST_LITERAL`; the compiler enforces this via `Record<IndexableFieldKind, string>`, so
|
|
38
39
|
* adding a member here fails the build until a CAST literal is supplied for it.
|
|
@@ -40,6 +41,7 @@ export declare const CONTENT_TYPE_SCALAR_KINDS: readonly ["text", "integer", "re
|
|
|
40
41
|
* `relation` stores a foreign entity id and shares `text`'s storage class, so its CAST target is
|
|
41
42
|
* the already-present literal `"TEXT"` — it adds a KEY to that table, never a new VALUE to the
|
|
42
43
|
* DDL alphabet.
|
|
44
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
43
45
|
*/
|
|
44
46
|
export declare const INDEXABLE_FIELD_KINDS: readonly ["text", "integer", "real", "boolean", "datetime", "relation"];
|
|
45
47
|
export type IndexableFieldKind = (typeof INDEXABLE_FIELD_KINDS)[number];
|
|
@@ -52,7 +54,7 @@ export type IndexableFieldKind = (typeof INDEXABLE_FIELD_KINDS)[number];
|
|
|
52
54
|
export declare const STORAGE_ONLY_FIELD_KINDS: readonly ["json"];
|
|
53
55
|
export type StorageOnlyFieldKind = (typeof STORAGE_ONLY_FIELD_KINDS)[number];
|
|
54
56
|
/**
|
|
55
|
-
*
|
|
57
|
+
* — the closed field-kind enum. UNCHANGED NAME AND MEANING for every existing
|
|
56
58
|
* consumer: it is still the complete, closed set of legal `kind` values, and
|
|
57
59
|
* {@link isContentTypeFieldKind} is still the gate for it.
|
|
58
60
|
*
|
|
@@ -61,6 +63,7 @@ export type StorageOnlyFieldKind = (typeof STORAGE_ONLY_FIELD_KINDS)[number];
|
|
|
61
63
|
* message. The spread order below preserves that; the published agent-tool schema
|
|
62
64
|
* (`agent-tools.ts`) also spreads this constant, so both widen in lockstep with no hand-copied
|
|
63
65
|
* second list to drift.
|
|
66
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
64
67
|
*/
|
|
65
68
|
export declare const CONTENT_TYPE_FIELD_KINDS: readonly ["text", "integer", "real", "boolean", "datetime", "relation", "json"];
|
|
66
69
|
export type ContentTypeFieldKind = (typeof CONTENT_TYPE_FIELD_KINDS)[number];
|
|
@@ -75,7 +78,11 @@ export type ContentTypeFieldKind = (typeof CONTENT_TYPE_FIELD_KINDS)[number];
|
|
|
75
78
|
* @complexity O(1) — a fixed-size array membership test.
|
|
76
79
|
* @overallScore 100
|
|
77
80
|
*/
|
|
78
|
-
export declare function isContentTypeFieldKind(
|
|
81
|
+
export declare function isContentTypeFieldKind(required: {
|
|
82
|
+
value: unknown;
|
|
83
|
+
}, _optional?: Record<string, never>): required is {
|
|
84
|
+
value: ContentTypeFieldKind;
|
|
85
|
+
};
|
|
79
86
|
/**
|
|
80
87
|
* Type guard for {@link IndexableFieldKind} — the gate `mapFieldKindToCast` applies instead of
|
|
81
88
|
* {@link isContentTypeFieldKind}, and the gate `write-service.ts` applies to reject
|
|
@@ -88,7 +95,11 @@ export declare function isContentTypeFieldKind(value: unknown): value is Content
|
|
|
88
95
|
* @complexity O(1) — a fixed-size array membership test.
|
|
89
96
|
* @overallScore 100
|
|
90
97
|
*/
|
|
91
|
-
export declare function isIndexableFieldKind(
|
|
98
|
+
export declare function isIndexableFieldKind(required: {
|
|
99
|
+
value: unknown;
|
|
100
|
+
}, _optional?: Record<string, never>): required is {
|
|
101
|
+
value: IndexableFieldKind;
|
|
102
|
+
};
|
|
92
103
|
export interface ContentTypeFieldDef {
|
|
93
104
|
name: string;
|
|
94
105
|
kind: ContentTypeFieldKind;
|
|
@@ -113,7 +124,7 @@ export interface ContentTypeRecord {
|
|
|
113
124
|
* applies to `AuthorizeFn`). `identity.PrincipalRecord.kind` assigns to it directly.
|
|
114
125
|
*/
|
|
115
126
|
export type ActorPrincipalKind = "user" | "agent" | "api_key" | "system";
|
|
116
|
-
/** The actor-identity envelope every chokepoint write in this package accepts
|
|
127
|
+
/** The actor-identity envelope every chokepoint write in this package accepts. See docs/decisions/DR-001-safe-schema-and-index-transitions.md. */
|
|
117
128
|
export interface ActorIdentityInput {
|
|
118
129
|
actorId: string;
|
|
119
130
|
/**
|
|
@@ -129,11 +140,4 @@ export interface ActorIdentityInput {
|
|
|
129
140
|
delegatedById?: string | null;
|
|
130
141
|
}
|
|
131
142
|
/** Generic success/failure envelope used across this package instead of throwing for expected rejections. */
|
|
132
|
-
export type Result<T, E> = {
|
|
133
|
-
ok: true;
|
|
134
|
-
value: T;
|
|
135
|
-
} | {
|
|
136
|
-
ok: false;
|
|
137
|
-
error: E;
|
|
138
|
-
};
|
|
139
143
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -24,15 +24,16 @@
|
|
|
24
24
|
* `features/content-types` domain vocabulary. No dependencies.
|
|
25
25
|
*/
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* — the closed, 5-entry INDEXABLE SCALAR set. Never extend ad hoc.
|
|
28
28
|
*
|
|
29
29
|
* Unchanged since the original enum: every value that has ever reached a `CAST` literal is still
|
|
30
30
|
* here, in this order. Kept as its own constant so the "these five are the original, untouched
|
|
31
31
|
* scalars" claim is expressible in code rather than only in a comment.
|
|
32
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
32
33
|
*/
|
|
33
34
|
export const CONTENT_TYPE_SCALAR_KINDS = ["text", "integer", "real", "boolean", "datetime"];
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* — kinds that MAY carry `queryable: true` and therefore MAY reach
|
|
36
37
|
* `mapFieldKindToCast`. Every member MUST have an entry in `index-provisioning.ts`'s
|
|
37
38
|
* `KIND_TO_CAST_LITERAL`; the compiler enforces this via `Record<IndexableFieldKind, string>`, so
|
|
38
39
|
* adding a member here fails the build until a CAST literal is supplied for it.
|
|
@@ -40,6 +41,7 @@ export const CONTENT_TYPE_SCALAR_KINDS = ["text", "integer", "real", "boolean",
|
|
|
40
41
|
* `relation` stores a foreign entity id and shares `text`'s storage class, so its CAST target is
|
|
41
42
|
* the already-present literal `"TEXT"` — it adds a KEY to that table, never a new VALUE to the
|
|
42
43
|
* DDL alphabet.
|
|
44
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
43
45
|
*/
|
|
44
46
|
export const INDEXABLE_FIELD_KINDS = [...CONTENT_TYPE_SCALAR_KINDS, "relation"];
|
|
45
47
|
/**
|
|
@@ -50,7 +52,7 @@ export const INDEXABLE_FIELD_KINDS = [...CONTENT_TYPE_SCALAR_KINDS, "relation"];
|
|
|
50
52
|
*/
|
|
51
53
|
export const STORAGE_ONLY_FIELD_KINDS = ["json"];
|
|
52
54
|
/**
|
|
53
|
-
*
|
|
55
|
+
* — the closed field-kind enum. UNCHANGED NAME AND MEANING for every existing
|
|
54
56
|
* consumer: it is still the complete, closed set of legal `kind` values, and
|
|
55
57
|
* {@link isContentTypeFieldKind} is still the gate for it.
|
|
56
58
|
*
|
|
@@ -59,6 +61,7 @@ export const STORAGE_ONLY_FIELD_KINDS = ["json"];
|
|
|
59
61
|
* message. The spread order below preserves that; the published agent-tool schema
|
|
60
62
|
* (`agent-tools.ts`) also spreads this constant, so both widen in lockstep with no hand-copied
|
|
61
63
|
* second list to drift.
|
|
64
|
+
* See docs/decisions/DR-001-safe-schema-and-index-transitions.md.
|
|
62
65
|
*/
|
|
63
66
|
export const CONTENT_TYPE_FIELD_KINDS = [
|
|
64
67
|
...INDEXABLE_FIELD_KINDS,
|
|
@@ -75,7 +78,8 @@ export const CONTENT_TYPE_FIELD_KINDS = [
|
|
|
75
78
|
* @complexity O(1) — a fixed-size array membership test.
|
|
76
79
|
* @overallScore 100
|
|
77
80
|
*/
|
|
78
|
-
export function isContentTypeFieldKind(
|
|
81
|
+
export function isContentTypeFieldKind(required, _optional = {}) {
|
|
82
|
+
const { value } = required;
|
|
79
83
|
return typeof value === "string" && CONTENT_TYPE_FIELD_KINDS.includes(value);
|
|
80
84
|
}
|
|
81
85
|
/**
|
|
@@ -90,7 +94,9 @@ export function isContentTypeFieldKind(value) {
|
|
|
90
94
|
* @complexity O(1) — a fixed-size array membership test.
|
|
91
95
|
* @overallScore 100
|
|
92
96
|
*/
|
|
93
|
-
export function isIndexableFieldKind(
|
|
97
|
+
export function isIndexableFieldKind(required, _optional = {}) {
|
|
98
|
+
const { value } = required;
|
|
94
99
|
return typeof value === "string" && INDEXABLE_FIELD_KINDS.includes(value);
|
|
95
100
|
}
|
|
101
|
+
/** Generic success/failure envelope used across this package instead of throwing for expected rejections. */
|
|
96
102
|
//# sourceMappingURL=types.js.map
|