@cosmicdrift/kumiko-framework 0.387.0 → 0.388.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/AGENTS.md +6 -1
- package/agents/examples/anonymous-access.md +1 -1
- package/agents/examples/apex-landing.md +204 -0
- package/agents/examples/api-worker-split.md +64 -0
- package/agents/examples/app-config.md +14 -0
- package/agents/examples/auth-mfa.md +115 -0
- package/agents/examples/env-composition.md +28 -1
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/prod-boot-options.md +16 -0
- package/agents/examples/rate-limiting.md +48 -0
- package/agents/examples/record-detail-layout.md +692 -0
- package/agents/examples/seed-migration.md +212 -0
- package/agents/examples/wizard-form.md +941 -0
- package/agents/examples/writeform-section.md +548 -0
- package/agents/features.md +19 -2
- package/agents/widgets.md +1 -0
- package/dist/api/client-error-route.js +8 -2
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +3 -1
- package/dist/api/request-context.d.ts +1 -1
- package/dist/api/request-context.js +1 -1
- package/dist/api/server-boot-checks.d.ts +10 -0
- package/dist/api/server-boot-checks.js +157 -0
- package/dist/api/server-event-dispatcher.d.ts +26 -0
- package/dist/api/server-event-dispatcher.js +208 -0
- package/dist/api/server-extra-routes.d.ts +11 -0
- package/dist/api/server-extra-routes.js +203 -0
- package/dist/api/server-middleware.d.ts +20 -0
- package/dist/api/server-middleware.js +211 -0
- package/dist/api/server-routes.d.ts +19 -0
- package/dist/api/server-routes.js +293 -0
- package/dist/api/server-websocket-routes.d.ts +4 -0
- package/dist/api/server-websocket-routes.js +108 -0
- package/dist/api/server-wiring.d.ts +32 -0
- package/dist/api/server-wiring.js +236 -0
- package/dist/api/server.d.ts +10 -15
- package/dist/api/server.js +48 -1251
- package/dist/api/session-cookies.js +4 -4
- package/dist/api/sse-route.js +16 -12
- package/dist/crypto/owner-transition-rekey.d.ts +5 -1
- package/dist/crypto/owner-transition-rekey.js +1 -1
- package/dist/crypto/pii-field-encryption.d.ts +5 -1
- package/dist/crypto/pii-field-encryption.js +3 -1
- package/dist/db/aggregate-sql.d.ts +21 -0
- package/dist/db/aggregate-sql.js +155 -0
- package/dist/db/closed-connection.d.ts +9 -0
- package/dist/db/closed-connection.js +95 -0
- package/dist/db/dialect.js +1 -1
- package/dist/db/distinct-on.d.ts +1 -1
- package/dist/db/driver-row-coercion.d.ts +2 -0
- package/dist/db/driver-row-coercion.js +186 -0
- package/dist/db/driver-value-preparation.d.ts +16 -0
- package/dist/db/driver-value-preparation.js +99 -0
- package/dist/db/entity-field-encryption.d.ts +1 -0
- package/dist/db/entity-field-encryption.js +1 -1
- package/dist/db/entity-table-meta.js +103 -110
- package/dist/db/event-store-executor-context.d.ts +2 -1
- package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
- package/dist/db/event-store-executor-read-aggregate.js +112 -0
- package/dist/db/event-store-executor-read-filter.d.ts +31 -0
- package/dist/db/event-store-executor-read-filter.js +203 -0
- package/dist/db/event-store-executor-read-paging.d.ts +14 -0
- package/dist/db/event-store-executor-read-paging.js +99 -0
- package/dist/db/event-store-executor-read-rows.d.ts +6 -0
- package/dist/db/event-store-executor-read-rows.js +32 -0
- package/dist/db/event-store-executor-read-search.d.ts +28 -0
- package/dist/db/event-store-executor-read-search.js +221 -0
- package/dist/db/event-store-executor-read-sort.d.ts +41 -0
- package/dist/db/event-store-executor-read-sort.js +130 -0
- package/dist/db/event-store-executor-read-where.d.ts +24 -0
- package/dist/db/event-store-executor-read-where.js +81 -0
- package/dist/db/event-store-executor-read.d.ts +2 -9
- package/dist/db/event-store-executor-read.js +48 -701
- package/dist/db/event-store-executor-write-steps.d.ts +20 -0
- package/dist/db/event-store-executor-write-steps.js +100 -0
- package/dist/db/event-store-executor-write.js +395 -499
- package/dist/db/index.d.ts +6 -2
- package/dist/db/index.js +5 -1
- package/dist/db/list-aggregate-labels.d.ts +14 -0
- package/dist/db/list-aggregate-labels.js +26 -0
- package/dist/db/migrate-generator.js +71 -86
- package/dist/db/migrate-runner.js +4 -1
- package/dist/db/query.d.ts +2 -15
- package/dist/db/query.js +8 -761
- package/dist/db/reference-data.js +6 -1
- package/dist/db/table-builder.js +113 -166
- package/dist/db/table-info.d.ts +10 -0
- package/dist/db/table-info.js +45 -0
- package/dist/db/temporal-value-guards.d.ts +6 -0
- package/dist/db/temporal-value-guards.js +15 -0
- package/dist/db/tenant-db-runner.d.ts +1 -0
- package/dist/db/tenant-db-runner.js +3 -0
- package/dist/db/tenant-db.js +39 -9
- package/dist/db/where-clause.d.ts +6 -0
- package/dist/db/where-clause.js +236 -0
- package/dist/derivatives/delete-stored-file.js +2 -1
- package/dist/engine/boot-validator/boot-check.js +1 -1
- package/dist/engine/boot-validator/detail-screens.js +2 -2
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +17 -0
- package/dist/engine/boot-validator/form-derivations.js +35 -1
- package/dist/engine/boot-validator/i18n-keys.js +5 -3
- package/dist/engine/boot-validator/index.js +2 -0
- package/dist/engine/boot-validator/pii-retention.js +233 -155
- package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
- package/dist/engine/boot-validator/screens-actions.js +8 -1
- package/dist/engine/boot-validator/screens-dashboard.js +2 -5
- package/dist/engine/boot-validator/screens-forms.js +2 -6
- package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.js +228 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +52 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
- package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
- package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
- package/dist/engine/boot-validator/screens-lists.js +12 -9
- package/dist/engine/boot-validator/screens.d.ts +0 -2
- package/dist/engine/boot-validator/screens.js +99 -860
- package/dist/engine/build-app-schema.js +56 -96
- package/dist/engine/define-feature.js +9 -2
- package/dist/engine/define-handler.js +1 -0
- package/dist/engine/entity-handlers.d.ts +13 -0
- package/dist/engine/entity-handlers.js +10 -21
- package/dist/engine/entity-list-runtime-options.d.ts +8 -0
- package/dist/engine/entity-list-runtime-options.js +18 -0
- package/dist/engine/feature-ast/extractors/handlers.js +1 -0
- package/dist/engine/feature-ast/extractors/round2.js +25 -6
- package/dist/engine/feature-ast/patch.js +4 -182
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +3 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +1 -0
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
- package/dist/engine/feature-ast/pattern-id-matchers.js +210 -0
- package/dist/engine/feature-ast/patterns.d.ts +9 -0
- package/dist/engine/feature-ast/patterns.js +43 -51
- package/dist/engine/feature-ast/render.js +48 -87
- package/dist/engine/feature-entity-handlers.js +3 -0
- package/dist/engine/form-derivations.js +18 -7
- package/dist/engine/handler-field-references.js +1 -1
- package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
- package/dist/engine/project-app-schema-for-roles.js +6 -4
- package/dist/engine/registry-ingest.js +1 -1
- package/dist/engine/schema-builder.js +228 -212
- package/dist/engine/screen-helpers.js +1 -1
- package/dist/engine/screen-kind.d.ts +7 -0
- package/dist/engine/screen-kind.js +1 -0
- package/dist/engine/strip-disabled-features-from-app-schema.js +51 -14
- package/dist/engine/validate-projection-allowlist.js +2 -1
- package/dist/entrypoint/index.d.ts +4 -1
- package/dist/entrypoint/index.js +7 -1
- package/dist/errors/docs-url.d.ts +1 -1
- package/dist/errors/docs-url.js +22 -2
- package/dist/es-ops/context.js +1 -1
- package/dist/event-store/admin-api.js +1 -1
- package/dist/event-store/event-store.js +10 -4
- package/dist/files/bind-pending-file-refs.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +4 -1
- package/dist/files/file-handle.js +2 -3
- package/dist/files/file-routes.js +11 -8
- package/dist/files/resolve-content-type.js +43 -0
- package/dist/files/upload-ledger.js +7 -0
- package/dist/i18n/required-surface-keys.js +182 -206
- package/dist/internal/stack-support.d.ts +2 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/jobs/job-runner-context.d.ts +32 -0
- package/dist/jobs/job-runner-context.js +305 -0
- package/dist/jobs/job-runner-execution.d.ts +7 -0
- package/dist/jobs/job-runner-execution.js +160 -0
- package/dist/jobs/job-runner-gates.d.ts +19 -0
- package/dist/jobs/job-runner-gates.js +198 -0
- package/dist/jobs/job-runner-helpers.d.ts +17 -3
- package/dist/jobs/job-runner-helpers.js +61 -2
- package/dist/jobs/job-runner-scope.d.ts +44 -0
- package/dist/jobs/job-runner-scope.js +102 -0
- package/dist/jobs/job-runner-types.d.ts +12 -0
- package/dist/jobs/job-runner.d.ts +0 -1
- package/dist/jobs/job-runner.js +47 -657
- package/dist/observability/escape-hatch-report.d.ts +3 -0
- package/dist/observability/escape-hatch-report.js +12 -1
- package/dist/pipeline/cascade-handler.js +165 -138
- package/dist/pipeline/dispatch-query.js +10 -3
- package/dist/pipeline/dispatch-shared.d.ts +13 -3
- package/dist/pipeline/dispatch-shared.js +58 -710
- package/dist/pipeline/dispatch-write.js +296 -189
- package/dist/pipeline/event-dispatcher-admin.js +2 -1
- package/dist/pipeline/event-dispatcher-delivery.js +3 -5
- package/dist/pipeline/handler-context-auth.d.ts +9 -0
- package/dist/pipeline/handler-context-auth.js +54 -0
- package/dist/pipeline/handler-context-config.d.ts +14 -0
- package/dist/pipeline/handler-context-config.js +113 -0
- package/dist/pipeline/handler-context-db.d.ts +24 -0
- package/dist/pipeline/handler-context-db.js +149 -0
- package/dist/pipeline/handler-context-dispatch.d.ts +21 -0
- package/dist/pipeline/handler-context-dispatch.js +89 -0
- package/dist/pipeline/handler-context-events.d.ts +43 -0
- package/dist/pipeline/handler-context-events.js +237 -0
- package/dist/pipeline/handler-context-notify.d.ts +12 -0
- package/dist/pipeline/handler-context-notify.js +55 -0
- package/dist/pipeline/handler-context-observability.d.ts +7 -0
- package/dist/pipeline/handler-context-observability.js +21 -0
- package/dist/pipeline/handler-context-scope.d.ts +48 -0
- package/dist/pipeline/handler-context-scope.js +88 -0
- package/dist/pipeline/notify-scope.js +12 -1
- package/dist/pipeline/pre-handler-gates.js +20 -4
- package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
- package/dist/pipeline/pre-transaction-reservations.js +9 -3
- package/dist/pipeline/projection-rebuild.js +2 -1
- package/dist/schema-cli/apply-command.d.ts +2 -0
- package/dist/schema-cli/apply-command.js +97 -0
- package/dist/schema-cli/baseline-command.d.ts +2 -0
- package/dist/schema-cli/baseline-command.js +33 -0
- package/dist/schema-cli/generate-command.d.ts +2 -0
- package/dist/schema-cli/generate-command.js +90 -0
- package/dist/schema-cli/shared.d.ts +39 -0
- package/dist/schema-cli/shared.js +95 -0
- package/dist/schema-cli/status-command.d.ts +2 -0
- package/dist/schema-cli/status-command.js +73 -0
- package/dist/schema-cli/validate-boot-input.d.ts +5 -0
- package/dist/schema-cli/validate-boot-input.js +130 -0
- package/dist/schema-cli/validate-command.d.ts +2 -0
- package/dist/schema-cli/validate-command.js +130 -0
- package/dist/schema-cli.d.ts +2 -18
- package/dist/schema-cli.js +47 -604
- package/dist/search/meilisearch-adapter.js +2 -1
- package/dist/upgrade-cli.js +149 -121
- package/package.json +4 -4
- package/src/changes.json +310 -0
- package/src/scripts/codemod/feature-layout.ts +10 -2
- package/src/scripts/codemod/object-form.ts +41 -4
- package/src/scripts/codemod/package-merge.ts +58 -8
- package/src/scripts/codemod/pii-personal-migration.ts +175 -145
- package/src/scripts/codemod/testing-injection.ts +122 -42
- package/src/scripts/codemod/testing-move.ts +29 -7
|
@@ -7,6 +7,7 @@ import type { RelationDefinition } from "../types/relations.ts";
|
|
|
7
7
|
import type { ScreenDefinition } from "../types/screen.ts";
|
|
8
8
|
import type { TreeActionDef } from "../types/tree-node.ts";
|
|
9
9
|
import type { WorkspaceDefinition } from "../types/workspace.ts";
|
|
10
|
+
import type { RawRefSentinel } from "./extractors/shared.ts";
|
|
10
11
|
import type { PatternChange } from "./patch.ts";
|
|
11
12
|
import type { SourceLocation } from "./source-location.ts";
|
|
12
13
|
export type PatternChangeIssue = {
|
|
@@ -26,6 +27,7 @@ export declare const PATTERN_SCHEMAS_BY_KIND: {
|
|
|
26
27
|
source: z.ZodType<SourceLocation, unknown, z.core.$ZodTypeInternals<SourceLocation, unknown>>;
|
|
27
28
|
entityName: z.ZodString;
|
|
28
29
|
definition: z.ZodType<EntityDefinition, unknown, z.core.$ZodTypeInternals<EntityDefinition, unknown>>;
|
|
30
|
+
backingTable: z.ZodOptional<z.ZodType<RawRefSentinel, unknown, z.core.$ZodTypeInternals<RawRefSentinel, unknown>>>;
|
|
29
31
|
}, z.core.$strict>;
|
|
30
32
|
relation: z.ZodObject<{
|
|
31
33
|
kind: z.ZodLiteral<"relation">;
|
|
@@ -648,6 +650,7 @@ export declare const patternSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
648
650
|
source: z.ZodType<SourceLocation, unknown, z.core.$ZodTypeInternals<SourceLocation, unknown>>;
|
|
649
651
|
entityName: z.ZodString;
|
|
650
652
|
definition: z.ZodType<EntityDefinition, unknown, z.core.$ZodTypeInternals<EntityDefinition, unknown>>;
|
|
653
|
+
backingTable: z.ZodOptional<z.ZodType<RawRefSentinel, unknown, z.core.$ZodTypeInternals<RawRefSentinel, unknown>>>;
|
|
651
654
|
}, z.core.$strict>, z.ZodObject<{
|
|
652
655
|
kind: z.ZodLiteral<"relation">;
|
|
653
656
|
source: z.ZodType<SourceLocation, unknown, z.core.$ZodTypeInternals<SourceLocation, unknown>>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type CallExpression } from "ts-morph";
|
|
2
|
+
import type { PatternId } from "./patch.ts";
|
|
3
|
+
import type { MembersByKind } from "./patterns.ts";
|
|
4
|
+
type PatternIdKind = PatternId["kind"];
|
|
5
|
+
type PatternIdOfKind = MembersByKind<PatternId>;
|
|
6
|
+
export declare function callMatchesId<K extends PatternIdKind>(call: CallExpression, kind: K, id: PatternIdOfKind[K]): boolean;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { SyntaxKind } from "ts-morph";
|
|
2
|
+
import { readNameLiteral, readNameOrRef, resolveSameFileObjectLiteral, } from "./extractors/shared.js";
|
|
3
|
+
const PATTERN_ID_MATCHERS = {
|
|
4
|
+
requires: matchSingletonCall,
|
|
5
|
+
optionalRequires: matchSingletonCall,
|
|
6
|
+
readsConfig: matchSingletonCall,
|
|
7
|
+
systemScope: matchSingletonCall,
|
|
8
|
+
toggleable: matchSingletonCall,
|
|
9
|
+
describe: matchSingletonCall,
|
|
10
|
+
uiHints: matchSingletonCall,
|
|
11
|
+
config: matchSingletonCall,
|
|
12
|
+
translations: matchSingletonCall,
|
|
13
|
+
authClaims: matchSingletonCall,
|
|
14
|
+
treeActions: matchSingletonCall,
|
|
15
|
+
entity: matchEntityCall,
|
|
16
|
+
relation: matchRelationCall,
|
|
17
|
+
nav: matchIdPropertyCall,
|
|
18
|
+
workspace: matchIdPropertyCall,
|
|
19
|
+
screen: matchIdPropertyCall,
|
|
20
|
+
writeHandler: matchHandlerCall,
|
|
21
|
+
queryHandler: matchHandlerCall,
|
|
22
|
+
streamHandler: matchHandlerCall,
|
|
23
|
+
hook: matchHookCall,
|
|
24
|
+
metric: matchShortNameCall,
|
|
25
|
+
secret: matchShortNameCall,
|
|
26
|
+
secretNamespace: matchShortNameCall,
|
|
27
|
+
claimKey: matchShortNameCall,
|
|
28
|
+
referenceData: matchReferenceDataCall,
|
|
29
|
+
useExtension: matchUseExtensionCall,
|
|
30
|
+
job: matchJobCall,
|
|
31
|
+
notification: matchNotificationCall,
|
|
32
|
+
httpRoute: matchHttpRouteCall,
|
|
33
|
+
projection: matchProjectionCall,
|
|
34
|
+
multiStreamProjection: matchProjectionCall,
|
|
35
|
+
defineEvent: matchDefineEventCall,
|
|
36
|
+
extendsRegistrar: matchExtendsRegistrarCall,
|
|
37
|
+
"ai.generate": matchAiStepCall,
|
|
38
|
+
"ai.extract": matchAiStepCall,
|
|
39
|
+
"ai.classify": matchAiStepCall,
|
|
40
|
+
};
|
|
41
|
+
export function callMatchesId(call, kind, id) {
|
|
42
|
+
return PATTERN_ID_MATCHERS[kind](call, id);
|
|
43
|
+
}
|
|
44
|
+
// Singletons: kind alone identifies the call.
|
|
45
|
+
function matchSingletonCall() {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
function matchEntityCall(call, id) {
|
|
49
|
+
return (matchFirstArgString(call, id.entityName) || matchObjectProperty(call, "name", id.entityName));
|
|
50
|
+
}
|
|
51
|
+
// Positional: r.relation(entity, name, def) | Object: { entity, name, ... }
|
|
52
|
+
function matchRelationCall(call, id) {
|
|
53
|
+
if (matchFirstArgString(call, id.entityName)) {
|
|
54
|
+
return matchArgString(call, 1, id.relationName);
|
|
55
|
+
}
|
|
56
|
+
return (matchObjectProperty(call, "entity", id.entityName) &&
|
|
57
|
+
matchObjectProperty(call, "name", id.relationName));
|
|
58
|
+
}
|
|
59
|
+
function matchIdPropertyCall(call, id) {
|
|
60
|
+
return matchObjectProperty(call, "id", id.id);
|
|
61
|
+
}
|
|
62
|
+
function matchHandlerCall(call, id) {
|
|
63
|
+
return (matchFirstArgString(call, id.handlerName) || matchObjectProperty(call, "name", id.handlerName));
|
|
64
|
+
}
|
|
65
|
+
// Positional: r.hook(type, target, fn) | Object: { type, target }
|
|
66
|
+
function matchHookCall(call, id) {
|
|
67
|
+
if (typeof id.target === "object") {
|
|
68
|
+
return matchHookAllOfCall(call, id.hookType, id.target.allOf);
|
|
69
|
+
}
|
|
70
|
+
if (matchFirstArgString(call, id.hookType)) {
|
|
71
|
+
return matchArgNameOrRef(call, 1, id.target);
|
|
72
|
+
}
|
|
73
|
+
return (matchObjectProperty(call, "type", id.hookType) && matchObjectProperty(call, "target", id.target));
|
|
74
|
+
}
|
|
75
|
+
// Entity-wide { allOf } target — only ever appears as the object
|
|
76
|
+
// literal { allOf: entity } (positional or nested in the object
|
|
77
|
+
// form), never as a bare string, so check both call shapes.
|
|
78
|
+
function matchHookAllOfCall(call, hookType, allOfEntity) {
|
|
79
|
+
if (matchFirstArgString(call, hookType)) {
|
|
80
|
+
return matchAllOfArg(call.getArguments()[1], allOfEntity);
|
|
81
|
+
}
|
|
82
|
+
return (matchObjectProperty(call, "type", hookType) &&
|
|
83
|
+
matchObjectAllOfProperty(call, "target", allOfEntity));
|
|
84
|
+
}
|
|
85
|
+
function matchShortNameCall(call, id) {
|
|
86
|
+
return matchFirstArgString(call, id.shortName) || matchObjectProperty(call, "name", id.shortName);
|
|
87
|
+
}
|
|
88
|
+
function matchReferenceDataCall(call, id) {
|
|
89
|
+
return (matchFirstArgString(call, id.entityName) || matchObjectProperty(call, "entity", id.entityName));
|
|
90
|
+
}
|
|
91
|
+
// Positional: r.useExtension(name, entity) | Object: { name, entity }
|
|
92
|
+
function matchUseExtensionCall(call, id) {
|
|
93
|
+
if (matchFirstArgString(call, id.extensionName)) {
|
|
94
|
+
return matchArgNameOrRef(call, 1, id.entityName);
|
|
95
|
+
}
|
|
96
|
+
return (matchObjectProperty(call, "name", id.extensionName) &&
|
|
97
|
+
matchObjectProperty(call, "entity", id.entityName));
|
|
98
|
+
}
|
|
99
|
+
function matchJobCall(call, id) {
|
|
100
|
+
return matchFirstArgString(call, id.jobName) || matchObjectProperty(call, "name", id.jobName);
|
|
101
|
+
}
|
|
102
|
+
function matchNotificationCall(call, id) {
|
|
103
|
+
return (matchFirstArgString(call, id.notificationName) ||
|
|
104
|
+
matchObjectProperty(call, "name", id.notificationName));
|
|
105
|
+
}
|
|
106
|
+
// Object form only; positional doesn't apply.
|
|
107
|
+
function matchHttpRouteCall(call, id) {
|
|
108
|
+
return (matchObjectProperty(call, "method", id.method) && matchObjectProperty(call, "path", id.path));
|
|
109
|
+
}
|
|
110
|
+
function matchProjectionCall(call, id) {
|
|
111
|
+
return matchObjectProperty(call, "name", id.name);
|
|
112
|
+
}
|
|
113
|
+
function matchDefineEventCall(call, id) {
|
|
114
|
+
return matchFirstArgString(call, id.eventName) || matchObjectProperty(call, "name", id.eventName);
|
|
115
|
+
}
|
|
116
|
+
function matchExtendsRegistrarCall(call, id) {
|
|
117
|
+
return matchFirstArgString(call, id.extensionName);
|
|
118
|
+
}
|
|
119
|
+
function matchAiStepCall(call, id) {
|
|
120
|
+
return readAiStepKey(call) === id.stepKey;
|
|
121
|
+
}
|
|
122
|
+
function readAiStepKey(call) {
|
|
123
|
+
const arg = call.getArguments()[0];
|
|
124
|
+
if (!arg)
|
|
125
|
+
return undefined;
|
|
126
|
+
const obj = resolveSameFileObjectLiteral(arg);
|
|
127
|
+
if (!obj)
|
|
128
|
+
return undefined;
|
|
129
|
+
const prop = obj.getProperty("stepKey");
|
|
130
|
+
if (!prop)
|
|
131
|
+
return undefined;
|
|
132
|
+
const assign = prop.asKind(SyntaxKind.PropertyAssignment);
|
|
133
|
+
if (assign) {
|
|
134
|
+
const init = assign.getInitializer();
|
|
135
|
+
if (!init)
|
|
136
|
+
return undefined;
|
|
137
|
+
return readNameLiteral(init);
|
|
138
|
+
}
|
|
139
|
+
const shorthand = prop.asKind(SyntaxKind.ShorthandPropertyAssignment);
|
|
140
|
+
if (shorthand)
|
|
141
|
+
return readNameLiteral(shorthand.getNameNode());
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
// Resolves an argument the same way the extractors do (readNameLiteral):
|
|
145
|
+
// a string literal directly, or a bare identifier following its
|
|
146
|
+
// declaration (same-file or imported) to a string-literal initializer —
|
|
147
|
+
// the dominant naming style in the framework's own bundled-features
|
|
148
|
+
// (`r.entity(ENTITY, ...)`, `r.useExtension(EXT_X, ...)`).
|
|
149
|
+
//
|
|
150
|
+
// Narrow on purpose: every kind's arg-0 (and relation's arg-1) is parsed
|
|
151
|
+
// via readNameLiteral, never readNameOrRef (see round2.ts/round3.ts) —
|
|
152
|
+
// widening this shared helper to readNameOrRef would let an object-form
|
|
153
|
+
// call's first argument (an ObjectLiteralExpression) match here too,
|
|
154
|
+
// short-circuiting the object-form branch in callMatchesId. Positions
|
|
155
|
+
// where the parser itself accepts an inline `{ name: "..." }` ref use
|
|
156
|
+
// `matchArgNameOrRef` below instead.
|
|
157
|
+
function matchArgString(call, index, expected) {
|
|
158
|
+
const arg = call.getArguments()[index];
|
|
159
|
+
if (!arg)
|
|
160
|
+
return false;
|
|
161
|
+
return readNameLiteral(arg) === expected;
|
|
162
|
+
}
|
|
163
|
+
// @wrapper-known semantic-alias
|
|
164
|
+
function matchFirstArgString(call, expected) {
|
|
165
|
+
return matchArgString(call, 0, expected);
|
|
166
|
+
}
|
|
167
|
+
// Like matchArgString, but via readNameOrRef — for the specific positional
|
|
168
|
+
// slots where the parser accepts an inline `{ name: "..." }` object ref in
|
|
169
|
+
// addition to a literal/identifier (useExtension's entity arg, hook's
|
|
170
|
+
// target arg; see round3.ts:445 / hooks.ts:75). Not a drop-in replacement
|
|
171
|
+
// for matchArgString: applying it to an arg-0 position would match an
|
|
172
|
+
// object-form call's first (and only) argument, see the comment above.
|
|
173
|
+
function matchArgNameOrRef(call, index, expected) {
|
|
174
|
+
const arg = call.getArguments()[index];
|
|
175
|
+
if (!arg)
|
|
176
|
+
return false;
|
|
177
|
+
return readNameOrRef(arg) === expected;
|
|
178
|
+
}
|
|
179
|
+
// Object-form property values are resolved via readNameOrRef, not the
|
|
180
|
+
// narrower readNameLiteral — some properties (useExtension's `entity`,
|
|
181
|
+
// hook's `target`/`allOf`) accept an inline `{ name: "..." }` ref in the
|
|
182
|
+
// parser (see extractors/round3.ts, extractors/hooks.ts), not just a
|
|
183
|
+
// literal or identifier. readNameOrRef is a superset of readNameLiteral,
|
|
184
|
+
// so this is safe for every other property too.
|
|
185
|
+
function matchObjectProperty(call, propName, expected) {
|
|
186
|
+
const obj = call.getArguments()[0]?.asKind(SyntaxKind.ObjectLiteralExpression);
|
|
187
|
+
if (!obj)
|
|
188
|
+
return false;
|
|
189
|
+
const init = obj.getProperty(propName)?.asKind(SyntaxKind.PropertyAssignment)?.getInitializer();
|
|
190
|
+
if (!init)
|
|
191
|
+
return false;
|
|
192
|
+
return readNameOrRef(init) === expected;
|
|
193
|
+
}
|
|
194
|
+
// Matches an r.hook `{ allOf: entity }` target — as the arg node directly
|
|
195
|
+
// (positional call form) or as a nested object property (object call form).
|
|
196
|
+
function matchAllOfArg(node, expectedEntity) {
|
|
197
|
+
const obj = node?.asKind(SyntaxKind.ObjectLiteralExpression);
|
|
198
|
+
const init = obj?.getProperty("allOf")?.asKind(SyntaxKind.PropertyAssignment)?.getInitializer();
|
|
199
|
+
if (!init)
|
|
200
|
+
return false;
|
|
201
|
+
return readNameOrRef(init) === expectedEntity;
|
|
202
|
+
}
|
|
203
|
+
function matchObjectAllOfProperty(call, propName, expectedEntity) {
|
|
204
|
+
const obj = call.getArguments()[0]?.asKind(SyntaxKind.ObjectLiteralExpression);
|
|
205
|
+
const propInit = obj
|
|
206
|
+
?.getProperty(propName)
|
|
207
|
+
?.asKind(SyntaxKind.PropertyAssignment)
|
|
208
|
+
?.getInitializer();
|
|
209
|
+
return matchAllOfArg(propInit, expectedEntity);
|
|
210
|
+
}
|
|
@@ -18,6 +18,7 @@ export type EntityPattern = {
|
|
|
18
18
|
readonly source: SourceLocation;
|
|
19
19
|
readonly entityName: string;
|
|
20
20
|
readonly definition: EntityDefinition;
|
|
21
|
+
readonly backingTable?: RawRefSentinel;
|
|
21
22
|
};
|
|
22
23
|
export type RelationPattern = {
|
|
23
24
|
readonly kind: "relation";
|
|
@@ -304,6 +305,14 @@ export type UnknownPattern = {
|
|
|
304
305
|
};
|
|
305
306
|
export type FeaturePattern = EntityPattern | RelationPattern | NavPattern | WorkspacePattern | ConfigPattern | TranslationsPattern | RequiresPattern | OptionalRequiresPattern | SystemScopePattern | ToggleablePattern | DescribePattern | UiHintsPattern | MetricPattern | SecretPattern | SecretNamespacePattern | ClaimKeyPattern | ReferenceDataPattern | ReadsConfigPattern | UseExtensionPattern | UsesApiPattern | ExposesApiPattern | TreeActionsPattern | ScreenPattern | WriteHandlerPattern | QueryHandlerPattern | StreamHandlerPattern | HookPattern | JobPattern | NotificationPattern | AuthClaimsPattern | HttpRoutePattern | ProjectionPattern | MultiStreamProjectionPattern | DefineEventPattern | ExtendsRegistrarPattern | EnvSchemaPattern | AiGeneratePattern | AiExtractPattern | AiClassifyPattern | UnknownPattern;
|
|
306
307
|
export type FeaturePatternKind = FeaturePattern["kind"];
|
|
308
|
+
export type MembersByKind<U extends {
|
|
309
|
+
readonly kind: string;
|
|
310
|
+
}> = {
|
|
311
|
+
readonly [K in U["kind"]]: Extract<U, {
|
|
312
|
+
readonly kind: K;
|
|
313
|
+
}>;
|
|
314
|
+
};
|
|
315
|
+
export type FeaturePatternOfKind = MembersByKind<FeaturePattern>;
|
|
307
316
|
export type Editability = "static" | "mixed" | "opaque";
|
|
308
317
|
export declare function getEditability(pattern: FeaturePattern): Editability;
|
|
309
318
|
export {};
|
|
@@ -59,56 +59,48 @@
|
|
|
59
59
|
// against this constant to decide "form vs read-only span" — the actual
|
|
60
60
|
// SourceLocation lives at the matching path in `opaqueProps`.
|
|
61
61
|
export const SCREEN_OPAQUE_MARKER = "$opaque";
|
|
62
|
+
const EDITABILITY_BY_KIND = {
|
|
63
|
+
entity: "static",
|
|
64
|
+
relation: "static",
|
|
65
|
+
nav: "static",
|
|
66
|
+
workspace: "static",
|
|
67
|
+
config: "static",
|
|
68
|
+
translations: "static",
|
|
69
|
+
requires: "static",
|
|
70
|
+
optionalRequires: "static",
|
|
71
|
+
systemScope: "static",
|
|
72
|
+
toggleable: "static",
|
|
73
|
+
describe: "static",
|
|
74
|
+
metric: "static",
|
|
75
|
+
secret: "static",
|
|
76
|
+
secretNamespace: "static",
|
|
77
|
+
claimKey: "static",
|
|
78
|
+
referenceData: "static",
|
|
79
|
+
readsConfig: "static",
|
|
80
|
+
useExtension: "static",
|
|
81
|
+
usesApi: "static",
|
|
82
|
+
exposesApi: "static",
|
|
83
|
+
treeActions: "static",
|
|
84
|
+
screen: "mixed",
|
|
85
|
+
writeHandler: "mixed",
|
|
86
|
+
queryHandler: "mixed",
|
|
87
|
+
streamHandler: "mixed",
|
|
88
|
+
hook: "mixed",
|
|
89
|
+
job: "mixed",
|
|
90
|
+
notification: "mixed",
|
|
91
|
+
httpRoute: "mixed",
|
|
92
|
+
projection: "mixed",
|
|
93
|
+
multiStreamProjection: "mixed",
|
|
94
|
+
defineEvent: "mixed",
|
|
95
|
+
"ai.generate": "mixed",
|
|
96
|
+
"ai.extract": "mixed",
|
|
97
|
+
"ai.classify": "mixed",
|
|
98
|
+
authClaims: "opaque",
|
|
99
|
+
extendsRegistrar: "opaque",
|
|
100
|
+
envSchema: "opaque",
|
|
101
|
+
uiHints: "opaque",
|
|
102
|
+
unknown: "opaque",
|
|
103
|
+
};
|
|
62
104
|
export function getEditability(pattern) {
|
|
63
|
-
|
|
64
|
-
case "entity":
|
|
65
|
-
case "relation":
|
|
66
|
-
case "nav":
|
|
67
|
-
case "workspace":
|
|
68
|
-
case "config":
|
|
69
|
-
case "translations":
|
|
70
|
-
case "requires":
|
|
71
|
-
case "optionalRequires":
|
|
72
|
-
case "systemScope":
|
|
73
|
-
case "toggleable":
|
|
74
|
-
case "describe":
|
|
75
|
-
case "metric":
|
|
76
|
-
case "secret":
|
|
77
|
-
case "secretNamespace":
|
|
78
|
-
case "claimKey":
|
|
79
|
-
case "referenceData":
|
|
80
|
-
case "readsConfig":
|
|
81
|
-
case "useExtension":
|
|
82
|
-
case "usesApi":
|
|
83
|
-
case "exposesApi":
|
|
84
|
-
case "treeActions":
|
|
85
|
-
return "static";
|
|
86
|
-
case "screen":
|
|
87
|
-
case "writeHandler":
|
|
88
|
-
case "queryHandler":
|
|
89
|
-
case "streamHandler":
|
|
90
|
-
case "hook":
|
|
91
|
-
case "job":
|
|
92
|
-
case "notification":
|
|
93
|
-
case "httpRoute":
|
|
94
|
-
case "projection":
|
|
95
|
-
case "multiStreamProjection":
|
|
96
|
-
case "defineEvent":
|
|
97
|
-
case "ai.generate":
|
|
98
|
-
case "ai.extract":
|
|
99
|
-
case "ai.classify":
|
|
100
|
-
return "mixed";
|
|
101
|
-
case "authClaims":
|
|
102
|
-
case "extendsRegistrar":
|
|
103
|
-
case "envSchema":
|
|
104
|
-
case "uiHints":
|
|
105
|
-
case "unknown":
|
|
106
|
-
return "opaque";
|
|
107
|
-
default: {
|
|
108
|
-
// Exhaustiveness guard — adding a new pattern kind without
|
|
109
|
-
// updating this switch produces a compile error here.
|
|
110
|
-
const _exhaustive = pattern;
|
|
111
|
-
return _exhaustive;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
105
|
+
return EDITABILITY_BY_KIND[pattern.kind];
|
|
114
106
|
}
|
|
@@ -30,92 +30,52 @@ export const VERSION_HEADER = `// kumiko-feature-version: ${FEATURE_FILE_VERSION
|
|
|
30
30
|
* trailing newline; callers compose statements with their own joiner.
|
|
31
31
|
*/
|
|
32
32
|
export function renderPattern(pattern) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return renderStreamHandler(pattern);
|
|
80
|
-
case "hook":
|
|
81
|
-
return renderHook(pattern);
|
|
82
|
-
case "job":
|
|
83
|
-
return renderJob(pattern);
|
|
84
|
-
case "notification":
|
|
85
|
-
return renderNotification(pattern);
|
|
86
|
-
case "authClaims":
|
|
87
|
-
return renderAuthClaims(pattern);
|
|
88
|
-
case "httpRoute":
|
|
89
|
-
return renderHttpRoute(pattern);
|
|
90
|
-
case "projection":
|
|
91
|
-
return renderProjection(pattern);
|
|
92
|
-
case "multiStreamProjection":
|
|
93
|
-
return renderMultiStreamProjection(pattern);
|
|
94
|
-
case "defineEvent":
|
|
95
|
-
return renderDefineEvent(pattern);
|
|
96
|
-
case "extendsRegistrar":
|
|
97
|
-
return renderExtendsRegistrar(pattern);
|
|
98
|
-
case "usesApi":
|
|
99
|
-
return renderUsesApi(pattern);
|
|
100
|
-
case "exposesApi":
|
|
101
|
-
return renderExposesApi(pattern);
|
|
102
|
-
case "treeActions":
|
|
103
|
-
return renderTreeActions(pattern);
|
|
104
|
-
case "envSchema":
|
|
105
|
-
return renderEnvSchema(pattern);
|
|
106
|
-
case "ai.generate":
|
|
107
|
-
return renderAiGenerate(pattern);
|
|
108
|
-
case "ai.extract":
|
|
109
|
-
return renderAiExtract(pattern);
|
|
110
|
-
case "ai.classify":
|
|
111
|
-
return renderAiClassify(pattern);
|
|
112
|
-
case "unknown":
|
|
113
|
-
return renderUnknown(pattern);
|
|
114
|
-
default: {
|
|
115
|
-
const _exhaustive = pattern;
|
|
116
|
-
return _exhaustive;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
33
|
+
return renderPatternOfKind(pattern.kind, pattern);
|
|
34
|
+
}
|
|
35
|
+
const PATTERN_RENDERERS = {
|
|
36
|
+
requires: renderRequires,
|
|
37
|
+
optionalRequires: renderOptionalRequires,
|
|
38
|
+
readsConfig: renderReadsConfig,
|
|
39
|
+
systemScope: renderSystemScope,
|
|
40
|
+
toggleable: renderToggleable,
|
|
41
|
+
describe: renderDescribe,
|
|
42
|
+
uiHints: renderUiHints,
|
|
43
|
+
entity: renderEntity,
|
|
44
|
+
relation: renderRelation,
|
|
45
|
+
nav: renderNav,
|
|
46
|
+
workspace: renderWorkspace,
|
|
47
|
+
config: renderConfig,
|
|
48
|
+
translations: renderTranslations,
|
|
49
|
+
metric: renderMetric,
|
|
50
|
+
secret: renderSecret,
|
|
51
|
+
secretNamespace: renderSecretNamespace,
|
|
52
|
+
claimKey: renderClaimKey,
|
|
53
|
+
referenceData: renderReferenceData,
|
|
54
|
+
useExtension: renderUseExtension,
|
|
55
|
+
screen: renderScreen,
|
|
56
|
+
writeHandler: renderWriteHandler,
|
|
57
|
+
queryHandler: renderQueryHandler,
|
|
58
|
+
streamHandler: renderStreamHandler,
|
|
59
|
+
hook: renderHook,
|
|
60
|
+
job: renderJob,
|
|
61
|
+
notification: renderNotification,
|
|
62
|
+
authClaims: renderAuthClaims,
|
|
63
|
+
httpRoute: renderHttpRoute,
|
|
64
|
+
projection: renderProjection,
|
|
65
|
+
multiStreamProjection: renderMultiStreamProjection,
|
|
66
|
+
defineEvent: renderDefineEvent,
|
|
67
|
+
extendsRegistrar: renderExtendsRegistrar,
|
|
68
|
+
usesApi: renderUsesApi,
|
|
69
|
+
exposesApi: renderExposesApi,
|
|
70
|
+
treeActions: renderTreeActions,
|
|
71
|
+
envSchema: renderEnvSchema,
|
|
72
|
+
"ai.generate": renderAiGenerate,
|
|
73
|
+
"ai.extract": renderAiExtract,
|
|
74
|
+
"ai.classify": renderAiClassify,
|
|
75
|
+
unknown: renderUnknown,
|
|
76
|
+
};
|
|
77
|
+
function renderPatternOfKind(kind, pattern) {
|
|
78
|
+
return PATTERN_RENDERERS[kind](pattern);
|
|
119
79
|
}
|
|
120
80
|
// =============================================================================
|
|
121
81
|
// JSON-Like Value Renderer — emits TypeScript-source-compatible literals.
|
|
@@ -206,7 +166,8 @@ function renderEntity(p) {
|
|
|
206
166
|
// losing `name` to the sentinel shortcut — spread the reference verbatim
|
|
207
167
|
// next to the name instead.
|
|
208
168
|
if (isRawRefSentinel(p.definition)) {
|
|
209
|
-
|
|
169
|
+
const backingTable = p.backingTable ? `, backingTable: ${p.backingTable.__raw}` : "";
|
|
170
|
+
return `r.entity({ name: ${JSON.stringify(p.entityName)}, ...${p.definition.__raw}${backingTable} });`;
|
|
210
171
|
}
|
|
211
172
|
const merged = { name: p.entityName, ...p.definition };
|
|
212
173
|
return `r.entity(${renderValue(merged)});`;
|
|
@@ -130,6 +130,9 @@ export function buildEntityHandlerMethods(state, name) {
|
|
|
130
130
|
...(def.unsafeSkipTransitionGuard && { unsafeSkipTransitionGuard: true }),
|
|
131
131
|
...(def.rateLimit && { rateLimit: def.rateLimit }),
|
|
132
132
|
...(def.additionalRateLimits && { additionalRateLimits: def.additionalRateLimits }),
|
|
133
|
+
...(def.rateLimitCost && {
|
|
134
|
+
rateLimitCost: def.rateLimitCost, // @cast-boundary engine-bridge
|
|
135
|
+
}),
|
|
133
136
|
...(def.escapeHatch && { escapeHatch: def.escapeHatch }),
|
|
134
137
|
...transactionPhaseSlots(def),
|
|
135
138
|
...(def.tenantlessAnonymous && { tenantlessAnonymous: true }),
|
|
@@ -101,17 +101,30 @@ function sumListCell(rows, listField, cell) {
|
|
|
101
101
|
return sum;
|
|
102
102
|
}
|
|
103
103
|
function targetCurrency(field, current, sourceCurrency) {
|
|
104
|
+
// The client value is last: it must never override what the field or its sources dictate.
|
|
105
|
+
if (field.type === "money" && field.currency.kind === "literal")
|
|
106
|
+
return field.currency.code;
|
|
107
|
+
if (sourceCurrency !== undefined)
|
|
108
|
+
return sourceCurrency;
|
|
104
109
|
if (isPlainObject(current) &&
|
|
105
110
|
typeof current["currency"] === "string" &&
|
|
106
111
|
current["currency"] !== "") {
|
|
107
112
|
return current["currency"];
|
|
108
113
|
}
|
|
109
|
-
if (sourceCurrency !== undefined)
|
|
110
|
-
return sourceCurrency;
|
|
111
|
-
if (field.type === "money" && field.currency.kind === "literal")
|
|
112
|
-
return field.currency.code;
|
|
113
114
|
return undefined;
|
|
114
115
|
}
|
|
116
|
+
function computeOrdinary(derivation, values, fields) {
|
|
117
|
+
const operands = derivation.from.map((name) => readOperand(values, fields, name));
|
|
118
|
+
const currencies = new Set(operands.flatMap((operand) => operand.currency ?? []));
|
|
119
|
+
const sourceCurrency = currencies.values().next().value;
|
|
120
|
+
// Operands in different currencies have no meaningful sum.
|
|
121
|
+
if (currencies.size > 1)
|
|
122
|
+
return { computed: undefined, sourceCurrency };
|
|
123
|
+
return {
|
|
124
|
+
computed: computeDerivedCellValue(derivation.op, operands.map((operand) => operand.value)),
|
|
125
|
+
sourceCurrency,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
115
128
|
/** Recomputes every derived head field of an action form from its inputs.
|
|
116
129
|
* Pure and client-safe: the renderer runs it live, the server runs it
|
|
117
130
|
* again (`withFormDerivations`) and overwrites whatever the client sent.
|
|
@@ -148,9 +161,7 @@ export function applyFormDerivations(values, screen) {
|
|
|
148
161
|
ordinary = derivation;
|
|
149
162
|
}
|
|
150
163
|
if (ordinary !== undefined) {
|
|
151
|
-
|
|
152
|
-
sourceCurrency = operands.find((operand) => operand.currency !== undefined)?.currency;
|
|
153
|
-
computed = computeDerivedCellValue(ordinary.op, operands.map((operand) => operand.value));
|
|
164
|
+
({ computed, sourceCurrency } = computeOrdinary(ordinary, result, screen.fields));
|
|
154
165
|
}
|
|
155
166
|
if (computed === undefined) {
|
|
156
167
|
delete result[target];
|
|
@@ -5,7 +5,7 @@ const MAX_WRAPPER_DEPTH = 8;
|
|
|
5
5
|
const MAX_NESTING_DEPTH = 8;
|
|
6
6
|
const referenceMetaSchema = z.union([
|
|
7
7
|
z.string().min(1),
|
|
8
|
-
z.
|
|
8
|
+
z.strictObject({ entity: z.string().min(1), by: z.string().min(1).optional() }),
|
|
9
9
|
]);
|
|
10
10
|
function rawReferenceMeta(field) {
|
|
11
11
|
const meta = zodMeta(field);
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { AppSchema } from "../ui-types/app-schema.ts";
|
|
2
|
+
import type { NavDefinition } from "./types/nav.ts";
|
|
2
3
|
export declare function projectAppSchemaForRoles(schema: AppSchema, roles: readonly string[]): AppSchema;
|
|
4
|
+
export declare function isPureGroupingNav(nav: Pick<NavDefinition, "screen" | "target" | "provider">): boolean;
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
// @cosmicdrift/kumiko-types/handlers) — an unset `access` here means every
|
|
28
28
|
// signed-in user may see the entry, the opposite stance from `hasAccess`.
|
|
29
29
|
//
|
|
30
|
-
// Reference-site coverage (every place boot-validator/screens
|
|
30
|
+
// Reference-site coverage (every place boot-validator/screens*.ts resolves a
|
|
31
31
|
// screen target is a site this file also projects):
|
|
32
32
|
// - nav.screen / nav.parent (author-registered AND settings-hub-generated
|
|
33
33
|
// navs — the latter use short ids relative to the synthetic "config"
|
|
@@ -204,16 +204,18 @@ function isNavDeniedOrDead(ctx, screenAccessByQn, keptScreenQns, roles) {
|
|
|
204
204
|
const effectiveAccess = ctx.nav.access ?? screenAccessByQn.get(ctx.resolvedScreenQn);
|
|
205
205
|
return !isUiAccessGranted(effectiveAccess, roles);
|
|
206
206
|
}
|
|
207
|
+
export function isPureGroupingNav(nav) {
|
|
208
|
+
return nav.screen === undefined && nav.target === undefined && nav.provider !== true;
|
|
209
|
+
}
|
|
207
210
|
function isDroppedByPropagation(ctx, dropped, childrenByParent) {
|
|
208
211
|
if (ctx.resolvedParentQn !== undefined && dropped.has(ctx.resolvedParentQn))
|
|
209
212
|
return true;
|
|
210
|
-
|
|
211
|
-
if (!isPureGroupingNode)
|
|
213
|
+
if (!isPureGroupingNav(ctx.nav))
|
|
212
214
|
return false;
|
|
213
215
|
const children = childrenByParent.get(ctx.qn) ?? [];
|
|
214
216
|
return children.length > 0 && children.every((child) => dropped.has(child.qn));
|
|
215
217
|
}
|
|
216
|
-
// --- screen-target resolution helpers (mirror boot-validator/screens
|
|
218
|
+
// --- screen-target resolution helpers (mirror boot-validator/screens*.ts) ---
|
|
217
219
|
function isScreenTargetKept(featureName, target, keptScreenQns) {
|
|
218
220
|
return keptScreenQns.has(resolveScreenTargetQn(featureName, target));
|
|
219
221
|
}
|
|
@@ -16,7 +16,7 @@ export function populateFeatureCore(state, feature) {
|
|
|
16
16
|
throw new Error(`Duplicate entity: "${name}" (registered by multiple features)`);
|
|
17
17
|
}
|
|
18
18
|
state.entityMap.set(name, entity);
|
|
19
|
-
const physical = resolveTableName(name, entity,
|
|
19
|
+
const physical = resolveTableName(name, entity, undefined);
|
|
20
20
|
const clash = state.physicalTableOwners.get(physical);
|
|
21
21
|
if (clash?.kind === "store") {
|
|
22
22
|
throw new Error(`Entity "${name}" (feature "${feature.name}") has physical table "${physical}" which ` +
|