@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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type normalizeListColumn } from "../screen-helpers.ts";
|
|
2
|
+
import type { ConfigKeyDefinition, EntityDefinition, FeatureDefinition } from "../types/index.ts";
|
|
3
|
+
import type { EditExtensionSection, EditFieldsSection, EditSectionSpec, RowAction } from "../types/screen.ts";
|
|
4
|
+
import type { NavigateSourceScreenType, NavigateTargetLookups } from "./screens-actions.ts";
|
|
5
|
+
import { type resolveRowActionNavigateTarget } from "./screens-actions.ts";
|
|
6
|
+
import type { ListActionTargetContext } from "./screens-lists.ts";
|
|
7
|
+
export type ScreenValidationContext = NavigateTargetLookups & {
|
|
8
|
+
readonly feature: FeatureDefinition;
|
|
9
|
+
readonly featureMap: ReadonlyMap<string, FeatureDefinition>;
|
|
10
|
+
readonly allWriteHandlerQns: ReadonlySet<string>;
|
|
11
|
+
readonly configKeyDefsByQn: ReadonlyMap<string, ConfigKeyDefinition>;
|
|
12
|
+
};
|
|
13
|
+
export declare function findEntityFeature(entityName: string, featureMap: ReadonlyMap<string, FeatureDefinition>): string | undefined;
|
|
14
|
+
export declare function buildUnknownFieldMessage(featureName: string, screenId: string, fieldName: string, entityName: string, knownFields: ReadonlySet<string>): string;
|
|
15
|
+
export declare function listActionContext(ctx: ScreenValidationContext, screenId: string, screenType: NavigateSourceScreenType, screenEntity: string | undefined, where: string): ListActionTargetContext;
|
|
16
|
+
export declare function resolveScreenEntity(ctx: ScreenValidationContext, screenId: string, entityName: string): EntityDefinition;
|
|
17
|
+
export declare function rejectMissingQuery(featureName: string, screenId: string, kind: "projectionList" | "projectionDetail", query: unknown): void;
|
|
18
|
+
export declare function rejectEmptyColumns(featureName: string, screenId: string, kind: "projectionList" | "entityList", columnCount: number): void;
|
|
19
|
+
export declare function validateListColumnRendering(ctx: ScreenValidationContext, screenId: string, kind: "projectionList" | "entityList", column: ReturnType<typeof normalizeListColumn>): void;
|
|
20
|
+
export declare function rejectNonArrayInFilter(featureName: string, screenId: string, kind: "projectionList" | "entityList", filter: {
|
|
21
|
+
readonly op: string;
|
|
22
|
+
readonly value?: unknown;
|
|
23
|
+
}): void;
|
|
24
|
+
export declare function rejectEmptySections(featureName: string, screenId: string, kind: "projectionDetail" | "configEdit" | "entityEdit", sectionCount: number): void;
|
|
25
|
+
export declare function assertExtensionSectionHasComponent(featureName: string, screenId: string, kind: "projectionDetail" | "configEdit" | "entityEdit", section: EditExtensionSection): void;
|
|
26
|
+
export declare function rejectProjectionDetailOnlySection(featureName: string, screenId: string, kind: "configEdit" | "entityEdit", section: Exclude<EditSectionSpec, EditExtensionSection>): asserts section is EditFieldsSection;
|
|
27
|
+
export declare function editSectionLabel(section: EditSectionSpec): string;
|
|
28
|
+
export declare function validateRecordSectionActions(ctx: ScreenValidationContext, screenId: string, kind: "projectionDetail" | "entityEdit", section: EditSectionSpec): void;
|
|
29
|
+
export declare function validateRecordHeaderAction(ctx: ScreenValidationContext, screenId: string, kind: "projectionDetail" | "entityEdit", screenEntity: string | undefined, action: RowAction): void;
|
|
30
|
+
type NavigateLink = {
|
|
31
|
+
readonly screen?: string;
|
|
32
|
+
readonly entity?: string;
|
|
33
|
+
readonly entityId?: string;
|
|
34
|
+
readonly tab?: string;
|
|
35
|
+
};
|
|
36
|
+
export declare function navigateActionFromLink(id: string, navigate: NavigateLink): Parameters<typeof resolveRowActionNavigateTarget>[4];
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Checks shared by several screen kinds; each helper takes the kind it reports under.
|
|
2
|
+
// Dependencies point one way: screens-kind-*.ts -> here ->
|
|
3
|
+
// screens-dashboard/forms/lists -> screens-actions.
|
|
4
|
+
import { qualifyEntityName } from "../qualified-name.js";
|
|
5
|
+
import { isWriteFormEditSection } from "../screen-helpers.js";
|
|
6
|
+
import { isRowClickAction, validateActionHasIcon, validateNavigateActionTab, validateRecordActionRedirect, } from "./screens-actions.js";
|
|
7
|
+
import { assertRefTargetRegistered, rejectInertColumnSortable, validateColumnRendererForm, validateRowActionTarget, } from "./screens-lists.js";
|
|
8
|
+
export function findEntityFeature(entityName, featureMap) {
|
|
9
|
+
for (const [name, feature] of featureMap) {
|
|
10
|
+
if (feature.entities?.[entityName])
|
|
11
|
+
return name;
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
export function buildUnknownFieldMessage(featureName, screenId, fieldName, entityName, knownFields) {
|
|
16
|
+
const known = [...knownFields].sort().join(", ");
|
|
17
|
+
return (`[Feature ${featureName}] Screen "${screenId}" references field "${fieldName}" ` +
|
|
18
|
+
`which does not exist on entity "${entityName}" (known: ${known}).`);
|
|
19
|
+
}
|
|
20
|
+
export function listActionContext(ctx, screenId, screenType, screenEntity, where) {
|
|
21
|
+
return {
|
|
22
|
+
featureName: ctx.feature.name,
|
|
23
|
+
screenId,
|
|
24
|
+
screenType,
|
|
25
|
+
screenEntity,
|
|
26
|
+
where,
|
|
27
|
+
screens: ctx.feature.screens,
|
|
28
|
+
allWriteHandlerQns: ctx.allWriteHandlerQns,
|
|
29
|
+
allScreenQns: ctx.allScreenQns,
|
|
30
|
+
navTargetShortIds: ctx.navTargetShortIds,
|
|
31
|
+
screensByShortId: ctx.screensByShortId,
|
|
32
|
+
detailForScreens: ctx.detailForScreens,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// entityList / entityEdit: entity-refs are feature-local.
|
|
36
|
+
export function resolveScreenEntity(ctx, screenId, entityName) {
|
|
37
|
+
const entityDef = ctx.feature.entities?.[entityName];
|
|
38
|
+
if (entityDef)
|
|
39
|
+
return entityDef;
|
|
40
|
+
const known = Object.keys(ctx.feature.entities ?? {})
|
|
41
|
+
.sort()
|
|
42
|
+
.join(", ") || "(none)";
|
|
43
|
+
const crossFeature = findEntityFeature(entityName, ctx.featureMap);
|
|
44
|
+
const hint = crossFeature
|
|
45
|
+
? ` Entity "${entityName}" is owned by feature "${crossFeature}" — cross-feature screen ownership is not supported.`
|
|
46
|
+
: "";
|
|
47
|
+
throw new Error(`[Feature ${ctx.feature.name}] Screen "${screenId}" references entity "${entityName}" ` +
|
|
48
|
+
`which is not declared in this feature (known: ${known}).${hint}`);
|
|
49
|
+
}
|
|
50
|
+
export function rejectMissingQuery(featureName, screenId, kind, query) {
|
|
51
|
+
if (!query || typeof query !== "string") {
|
|
52
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) has empty or non-string query. Set query to a registered query name.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// An empty column list would render a blank table: almost always an
|
|
56
|
+
// in-progress screen the author forgot to fill in.
|
|
57
|
+
export function rejectEmptyColumns(featureName, screenId, kind, columnCount) {
|
|
58
|
+
if (columnCount === 0) {
|
|
59
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) has an empty columns list — ` +
|
|
60
|
+
`declare at least one column.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function validateListColumnRendering(ctx, screenId, kind, column) {
|
|
64
|
+
const featureName = ctx.feature.name;
|
|
65
|
+
validateColumnRendererForm(featureName, screenId, column);
|
|
66
|
+
rejectInertColumnSortable(featureName, screenId, kind, column);
|
|
67
|
+
if (column.refEntity !== undefined) {
|
|
68
|
+
assertRefTargetRegistered(`[Feature ${featureName}] Screen "${screenId}" (${kind})`, `column "${column.field}" (refEntity)`, column.refEntity, featureName, ctx.featureMap);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function rejectNonArrayInFilter(featureName, screenId, kind, filter) {
|
|
72
|
+
if (filter.op === "in" && !Array.isArray(filter.value)) {
|
|
73
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) filter.op "in" requires ` +
|
|
74
|
+
`filter.value to be a readonly array. Pass an array as filter.value.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// A layout with zero sections renders as nothing: reject at boot so the
|
|
78
|
+
// author sees it before the blank form surprises them.
|
|
79
|
+
export function rejectEmptySections(featureName, screenId, kind, sectionCount) {
|
|
80
|
+
if (sectionCount === 0) {
|
|
81
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) has an empty sections list — ` +
|
|
82
|
+
`declare at least one section.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function assertExtensionSectionHasComponent(featureName, screenId, kind, section) {
|
|
86
|
+
if (section.component?.react === undefined && section.component?.native === undefined) {
|
|
87
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) extension section ` +
|
|
88
|
+
`"${section.title}" has no component — declare a react/native component marker.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// relatedList and writeForm sections exist on projectionDetail only; what
|
|
92
|
+
// remains afterwards is a plain fields/groups section.
|
|
93
|
+
export function rejectProjectionDetailOnlySection(featureName, screenId, kind, section) {
|
|
94
|
+
if (section.kind === "relatedList") {
|
|
95
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) relatedList section ` +
|
|
96
|
+
`"${section.title}" is not supported — relatedList is a projectionDetail-only ` +
|
|
97
|
+
`primitive (fw#2166). Move the section to a projectionDetail screen or remove it.`);
|
|
98
|
+
}
|
|
99
|
+
if (isWriteFormEditSection(section)) {
|
|
100
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${kind}) writeForm section ` +
|
|
101
|
+
`"${section.title}" is not supported — writeForm is a projectionDetail-only primitive. Move the section to a projectionDetail screen or remove it.`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export function editSectionLabel(section) {
|
|
105
|
+
return section.title ?? section.id ?? "(untitled)";
|
|
106
|
+
}
|
|
107
|
+
export function validateRecordSectionActions(ctx, screenId, kind, section) {
|
|
108
|
+
const actionContext = `section "${editSectionLabel(section)}" action`;
|
|
109
|
+
for (const action of section.actions ?? []) {
|
|
110
|
+
validateActionHasIcon(ctx.feature.name, screenId, kind, actionContext, action);
|
|
111
|
+
validateRecordActionRedirect(ctx.feature, screenId, kind, action, ctx);
|
|
112
|
+
validateNavigateActionTab(ctx.feature.name, screenId, kind, actionContext, action, ctx);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const NO_ROW_SUBJECT = {
|
|
116
|
+
projectionDetail: "a detail screen",
|
|
117
|
+
entityEdit: "an edit screen",
|
|
118
|
+
};
|
|
119
|
+
// Header actions reuse RowAction (the displayed record stands in for the row),
|
|
120
|
+
// so the same navigate/writeHandler existence checks as list rowActions apply.
|
|
121
|
+
// `rowClick` is rejected outright: there is no row to click.
|
|
122
|
+
export function validateRecordHeaderAction(ctx, screenId, kind, screenEntity, action) {
|
|
123
|
+
const featureName = ctx.feature.name;
|
|
124
|
+
validateRecordActionRedirect(ctx.feature, screenId, kind, action, ctx);
|
|
125
|
+
if (isRowClickAction(action)) {
|
|
126
|
+
throw new Error(`[Feature ${featureName}] Screen "${qualifyEntityName(featureName, "screen", screenId)}" ` +
|
|
127
|
+
`(${kind}) action "${action.id}" sets rowClick: true — there is no row to ` +
|
|
128
|
+
`click on ${NO_ROW_SUBJECT[kind]}. Remove rowClick.`);
|
|
129
|
+
}
|
|
130
|
+
validateRowActionTarget(listActionContext(ctx, screenId, kind, screenEntity, `Screen "${screenId}" (${kind})`), "action", "rowAction", action);
|
|
131
|
+
}
|
|
132
|
+
export function navigateActionFromLink(id, navigate) {
|
|
133
|
+
return {
|
|
134
|
+
kind: "navigate",
|
|
135
|
+
id,
|
|
136
|
+
label: id,
|
|
137
|
+
...(navigate.screen !== undefined ? { screen: navigate.screen } : {}),
|
|
138
|
+
...(navigate.entity !== undefined ? { entity: navigate.entity } : {}),
|
|
139
|
+
...(navigate.entityId !== undefined ? { entityId: navigate.entityId } : {}),
|
|
140
|
+
...(navigate.tab !== undefined ? { tab: navigate.tab } : {}),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -24,7 +24,7 @@ type RelatedListSectionValidation = {
|
|
|
24
24
|
}>;
|
|
25
25
|
};
|
|
26
26
|
export declare function validateRelatedListSection(args: RelatedListSectionValidation): void;
|
|
27
|
-
type ListActionTargetContext = NavigateTargetLookups & {
|
|
27
|
+
export type ListActionTargetContext = NavigateTargetLookups & {
|
|
28
28
|
readonly featureName: string;
|
|
29
29
|
readonly screenId: string;
|
|
30
30
|
readonly screenType: NavigateSourceScreenType;
|
|
@@ -34,6 +34,7 @@ type ListActionTargetContext = NavigateTargetLookups & {
|
|
|
34
34
|
readonly allWriteHandlerQns: ReadonlySet<string>;
|
|
35
35
|
};
|
|
36
36
|
export declare function validateListActionTargets(ctx: ListActionTargetContext, actionLabel: "rowAction" | "emptyState action", action: RowAction): void;
|
|
37
|
+
export declare function validateRowActionTarget(ctx: ListActionTargetContext, actionLabel: "rowAction" | "emptyState action" | "action", paramsLabel: "rowAction" | "emptyState action", action: RowAction): void;
|
|
37
38
|
export declare function rejectInertColumnSortable(featureName: string, screenId: string, screenType: "entityList" | "projectionList", column: {
|
|
38
39
|
readonly field: string;
|
|
39
40
|
readonly sortable?: boolean;
|
|
@@ -2,7 +2,7 @@ import { REFERENCE_LOOKUP_SOURCES } from "../../ui-types/list-row-meta.js";
|
|
|
2
2
|
import { parseRefTarget } from "../parse-ref-target.js";
|
|
3
3
|
import { qualifyEntityName } from "../qualified-name.js";
|
|
4
4
|
import { explicitListScreenId, normalizeListColumn, resolveNavParentScreen, } from "../screen-helpers.js";
|
|
5
|
-
import { isRowClickAction, rejectRedirectOnListRowAction, resolveRowActionNavigateTarget, validateClientActionName, validateDrawerTargetAction, validateRowActionNavigateParams, } from "./screens-actions.js";
|
|
5
|
+
import { assertWriteHandlerRegistered, isRowClickAction, rejectRedirectOnListRowAction, resolveRowActionNavigateTarget, validateClientActionName, validateDrawerTargetAction, validateRowActionNavigateParams, } from "./screens-actions.js";
|
|
6
6
|
// validateScreenHasNavArea runs once per screen; the index is built once per
|
|
7
7
|
// featureMap (immutable during boot) instead of re-scanning every feature each call.
|
|
8
8
|
const navAreaIndexByFeatureMap = new WeakMap();
|
|
@@ -115,13 +115,19 @@ function rejectEmptyStateActionParams(ctx, actionLabel, action) {
|
|
|
115
115
|
// Target checks shared by list/related-list rowActions and emptyState.action:
|
|
116
116
|
// navigate target + params, drawer target, write-handler registration.
|
|
117
117
|
export function validateListActionTargets(ctx, actionLabel, action) {
|
|
118
|
-
|
|
119
|
-
rejectRedirectOnListRowAction(featureName, screenId, screenType, action);
|
|
118
|
+
rejectRedirectOnListRowAction(ctx.featureName, ctx.screenId, ctx.screenType, action);
|
|
120
119
|
if (actionLabel === "emptyState action")
|
|
121
120
|
rejectEmptyStateActionParams(ctx, actionLabel, action);
|
|
121
|
+
validateRowActionTarget(ctx, actionLabel, actionLabel, action);
|
|
122
|
+
}
|
|
123
|
+
// Header actions have always reported navigate-params errors under the
|
|
124
|
+
// "rowAction" label, which validateRowActionNavigateParams only knows in list
|
|
125
|
+
// labels — hence paramsLabel apart from actionLabel.
|
|
126
|
+
export function validateRowActionTarget(ctx, actionLabel, paramsLabel, action) {
|
|
127
|
+
const { featureName, screenId, screenType } = ctx;
|
|
122
128
|
if (action.kind === "navigate") {
|
|
123
129
|
const target = resolveRowActionNavigateTarget(featureName, screenId, screenType, actionLabel, action, ctx.allScreenQns, ctx.navTargetShortIds, ctx.screensByShortId, ctx.detailForScreens);
|
|
124
|
-
validateRowActionNavigateParams(featureName, screenId, screenType, ctx.screenEntity, action, target,
|
|
130
|
+
validateRowActionNavigateParams(featureName, screenId, screenType, ctx.screenEntity, action, target, paramsLabel);
|
|
125
131
|
}
|
|
126
132
|
else if (action.kind === "drawer") {
|
|
127
133
|
validateDrawerTargetAction(featureName, screenId, screenType, actionLabel, action, ctx.screens);
|
|
@@ -129,11 +135,8 @@ export function validateListActionTargets(ctx, actionLabel, action) {
|
|
|
129
135
|
else if (action.kind === "clientAction") {
|
|
130
136
|
validateClientActionName(featureName, screenId, screenType, actionLabel, action);
|
|
131
137
|
}
|
|
132
|
-
else
|
|
133
|
-
|
|
134
|
-
`${actionLabel} "${action.id}" handler "${action.handler}" ` +
|
|
135
|
-
`is not a registered write-handler. Check the QN spelling (expected ` +
|
|
136
|
-
`"<feature>:write:<short>") and that the handler is declared via r.writeHandler(...).`);
|
|
138
|
+
else {
|
|
139
|
+
assertWriteHandlerRegistered(featureName, `${ctx.where} ${actionLabel} "${action.id}"`, action.handler, ctx.allWriteHandlerQns);
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
142
|
function validateRelatedListRowActions(args, rowActions) {
|
|
@@ -10,8 +10,6 @@ export declare function validateScreens(feature: FeatureDefinition, featureMap:
|
|
|
10
10
|
readonly featureName: string;
|
|
11
11
|
readonly screen: ScreenDefinition;
|
|
12
12
|
}>): void;
|
|
13
|
-
export declare function findEntityFeature(entityName: string, featureMap: ReadonlyMap<string, FeatureDefinition>): string | undefined;
|
|
14
|
-
export declare function buildUnknownFieldMessage(featureName: string, screenId: string, fieldName: string, entityName: string, knownFields: ReadonlySet<string>): string;
|
|
15
13
|
export declare function collectScreenQns(features: readonly FeatureDefinition[]): Set<string>;
|
|
16
14
|
export declare function screenShortIdsFrom(allScreenQns: ReadonlySet<string>): Set<string>;
|
|
17
15
|
export declare function collectScreensByShortId(features: readonly FeatureDefinition[]): Map<string, ReadonlyArray<{
|