@cosmicdrift/kumiko-framework 0.388.0 → 0.389.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 +3 -0
- package/agents/examples/golden-path.md +13 -1
- package/agents/examples/lifecycle-hooks.md +55 -2
- package/agents/examples/list-table-options.md +466 -0
- package/agents/examples/pdf-audit-export.md +4 -2
- package/agents/examples/state-machine.md +3 -2
- package/agents/examples/tenant-isolation.md +73 -1
- package/agents/examples.md +1 -0
- package/agents/widgets.md +1 -0
- package/dist/api/api-constants.d.ts +1 -0
- package/dist/api/api-constants.js +2 -0
- package/dist/api/auth-routes-shared.d.ts +2 -0
- package/dist/api/auth-routes-shared.js +5 -0
- package/dist/api/client-error-route.js +2 -3
- package/dist/api/entity-read-access.d.ts +2 -1
- package/dist/api/entity-read-access.js +14 -0
- package/dist/api/export/export-cells-reference.d.ts +9 -0
- package/dist/api/export/export-cells-reference.js +21 -0
- package/dist/api/export/export-cells.d.ts +11 -0
- package/dist/api/export/export-cells.js +136 -0
- package/dist/api/export/export-ciphertext-guard.d.ts +2 -0
- package/dist/api/export/export-ciphertext-guard.js +27 -0
- package/dist/api/export/export-columns.d.ts +29 -0
- package/dist/api/export/export-columns.js +85 -0
- package/dist/api/export/export-errors.d.ts +4 -0
- package/dist/api/export/export-errors.js +14 -0
- package/dist/api/export/export-route.d.ts +4 -0
- package/dist/api/export/export-route.js +74 -0
- package/dist/api/export/export-rows.d.ts +13 -0
- package/dist/api/export/export-rows.js +62 -0
- package/dist/api/export/export-screen.d.ts +12 -0
- package/dist/api/export/export-screen.js +47 -0
- package/dist/api/export/export-xlsx-writer.d.ts +4 -0
- package/dist/api/export/export-xlsx-writer.js +21 -0
- package/dist/api/extra-route.d.ts +4 -0
- package/dist/api/pii-leak-guard.d.ts +3 -0
- package/dist/api/pii-leak-guard.js +3 -2
- package/dist/api/routes.d.ts +16 -0
- package/dist/api/routes.js +2 -2
- package/dist/api/server-extra-routes.js +3 -0
- package/dist/api/server-routes.d.ts +1 -1
- package/dist/api/server-routes.js +8 -2
- package/dist/api/server-wiring.js +2 -0
- package/dist/api/sse-route.d.ts +2 -1
- package/dist/api/sse-route.js +11 -1
- package/dist/config/index.d.ts +3 -1
- package/dist/crypto/pg-kms-adapter.d.ts +1 -0
- package/dist/crypto/pg-kms-adapter.js +12 -18
- package/dist/db/aggregate-sql.d.ts +2 -0
- package/dist/db/aggregate-sql.js +10 -0
- package/dist/db/closed-connection.js +12 -19
- package/dist/db/connection.d.ts +4 -5
- package/dist/db/connection.js +29 -20
- package/dist/db/driver-row-coercion.js +4 -4
- package/dist/db/driver-value-preparation.js +6 -4
- package/dist/db/event-store-executor-read-aggregate-currencies.d.ts +17 -0
- package/dist/db/event-store-executor-read-aggregate-currencies.js +40 -0
- package/dist/db/event-store-executor-read-aggregate.js +36 -5
- package/dist/db/event-store-executor-read-filter.js +6 -3
- package/dist/db/event-store-executor-write-atomic.d.ts +4 -0
- package/dist/db/event-store-executor-write-atomic.js +41 -0
- package/dist/db/event-store-executor-write-steps.d.ts +0 -3
- package/dist/db/event-store-executor-write-steps.js +0 -5
- package/dist/db/event-store-executor-write.js +15 -15
- package/dist/db/event-store-executor.js +2 -1
- package/dist/db/executor-managed-tables.js +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/migrate-runner.js +3 -3
- package/dist/db/pg-error.js +26 -21
- package/dist/db/queries/backfill-pii.js +3 -6
- package/dist/db/queries/database-now.js +2 -1
- package/dist/db/queries/shadow-swap.js +2 -3
- package/dist/db/query.d.ts +14 -0
- package/dist/db/query.js +68 -43
- package/dist/db/reference-data.js +14 -2
- package/dist/db/statement-timeout.js +2 -1
- package/dist/db/tenant-db-upsert.d.ts +5 -0
- package/dist/db/tenant-db-upsert.js +91 -0
- package/dist/db/tenant-db.js +11 -1
- package/dist/engine/boot-validator/bundled-lists-table.d.ts +2 -0
- package/dist/engine/boot-validator/bundled-lists-table.js +36 -0
- package/dist/engine/boot-validator/entity-list-aggregate-options.d.ts +3 -0
- package/dist/engine/boot-validator/entity-list-aggregate-options.js +23 -0
- package/dist/engine/boot-validator/list-bulk-actions.d.ts +3 -0
- package/dist/engine/boot-validator/list-bulk-actions.js +49 -0
- package/dist/engine/boot-validator/list-column-layout.d.ts +8 -0
- package/dist/engine/boot-validator/list-column-layout.js +41 -0
- package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +24 -0
- package/dist/engine/boot-validator/list-table-aggregate-options.js +145 -0
- package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +10 -0
- package/dist/engine/boot-validator/list-table-aggregate-query.js +31 -0
- package/dist/engine/boot-validator/list-table-export-options.d.ts +1 -0
- package/dist/engine/boot-validator/list-table-export-options.js +27 -0
- package/dist/engine/boot-validator/list-table-options.d.ts +3 -0
- package/dist/engine/boot-validator/list-table-options.js +77 -0
- package/dist/engine/boot-validator/list-table-reject-unknown-keys.d.ts +1 -0
- package/dist/engine/boot-validator/list-table-reject-unknown-keys.js +7 -0
- package/dist/engine/boot-validator/projection-list-screens.d.ts +1 -1
- package/dist/engine/boot-validator/projection-list-screens.js +3 -13
- package/dist/engine/boot-validator/related-list-table-rules.d.ts +4 -0
- package/dist/engine/boot-validator/related-list-table-rules.js +42 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.js +7 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +4 -0
- package/dist/engine/boot-validator/screens-lists.js +12 -2
- package/dist/engine/build-app-schema.d.ts +2 -1
- package/dist/engine/build-app-schema.js +9 -1
- package/dist/engine/build-config-feature-schema.js +6 -1
- package/dist/engine/define-feature.js +4 -0
- package/dist/engine/feature-ast/extractors/index.d.ts +1 -1
- package/dist/engine/feature-ast/extractors/index.js +1 -1
- package/dist/engine/feature-ast/extractors/round1.d.ts +2 -1
- package/dist/engine/feature-ast/extractors/round1.js +6 -0
- package/dist/engine/feature-ast/index.d.ts +1 -1
- package/dist/engine/feature-ast/parse.js +3 -1
- package/dist/engine/feature-ast/patch.d.ts +2 -0
- package/dist/engine/feature-ast/patch.js +1 -0
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +7 -245
- package/dist/engine/feature-ast/pattern-change-schema.js +9 -163
- package/dist/engine/feature-ast/pattern-id-matchers.js +1 -0
- package/dist/engine/feature-ast/pattern-id-schema.d.ts +251 -0
- package/dist/engine/feature-ast/pattern-id-schema.js +166 -0
- package/dist/engine/feature-ast/patterns.d.ts +5 -1
- package/dist/engine/feature-ast/patterns.js +1 -0
- package/dist/engine/feature-ast/render.js +4 -0
- package/dist/engine/feature-builder-state.d.ts +1 -0
- package/dist/engine/feature-builder-state.js +1 -0
- package/dist/engine/feature-manifest.d.ts +1 -0
- package/dist/engine/feature-manifest.js +1 -0
- package/dist/engine/field-access.d.ts +1 -0
- package/dist/engine/field-access.js +12 -0
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/engine/pattern-library/library.js +2 -1
- package/dist/engine/pattern-library/static-schemas.d.ts +1 -0
- package/dist/engine/pattern-library/static-schemas.js +9 -0
- package/dist/engine/screen-filter-ops.js +2 -0
- package/dist/engine/strip-disabled-features-from-app-schema.js +7 -3
- package/dist/engine/types/feature.d.ts +1 -0
- package/dist/engine/types/index.d.ts +4 -3
- package/dist/engine/types/screen.d.ts +1 -1
- package/dist/engine/types/screen.js +1 -1
- package/dist/entrypoint/index.d.ts +2 -0
- package/dist/entrypoint/index.js +3 -0
- package/dist/event-store/event-store.d.ts +1 -0
- package/dist/event-store/event-store.js +7 -4
- package/dist/event-store/provenance-append.js +1 -1
- package/dist/files/create-file-ref-from-bytes.js +3 -0
- package/dist/files/create-file-ref.d.ts +3 -0
- package/dist/files/create-file-ref.js +11 -2
- package/dist/files/file-routes.d.ts +2 -0
- package/dist/files/file-routes.js +29 -2
- package/dist/files/provider-resolver.d.ts +1 -0
- package/dist/files/provider-resolver.js +1 -1
- package/dist/files/record-write-check.d.ts +6 -0
- package/dist/files/record-write-check.js +13 -0
- package/dist/files/upload-ledger.d.ts +2 -2
- package/dist/files/upload-ledger.js +4 -4
- package/dist/i18n/required-surface-keys.js +7 -2
- package/dist/internal/testing.js +1 -1
- package/dist/jobs/job-runner-scope.js +6 -6
- package/dist/jobs/job-runner.js +3 -0
- package/dist/observability/escape-hatch-report.js +9 -2
- package/dist/pipeline/after-rollback-sink.d.ts +6 -0
- package/dist/pipeline/after-rollback-sink.js +32 -0
- package/dist/pipeline/dispatch-batch.js +8 -1
- package/dist/pipeline/dispatch-shared.js +4 -0
- package/dist/pipeline/event-dispatcher-delivery.js +3 -1
- package/dist/pipeline/event-dispatcher.d.ts +1 -0
- package/dist/pipeline/event-dispatcher.js +6 -9
- package/dist/pipeline/handler-context-dispatch.d.ts +2 -0
- package/dist/pipeline/handler-context-dispatch.js +13 -1
- package/dist/pipeline/handler-context-events.d.ts +2 -4
- package/dist/pipeline/handler-context-events.js +6 -4
- package/dist/pipeline/system-hooks.d.ts +3 -1
- package/dist/pipeline/system-hooks.js +55 -6
- package/dist/pipeline/write-result-masking.js +13 -10
- package/dist/schema-cli/baseline-command.js +1 -1
- package/dist/schema-cli/status-command.js +1 -1
- package/dist/search/reindex-entity.js +15 -5
- package/dist/ui-types/index.d.ts +2 -2
- package/dist/ui-types/index.js +1 -1
- package/dist/upgrade-changelog.d.ts +2 -0
- package/dist/upgrade-changelog.js +14 -0
- package/dist/upgrade-cli.d.ts +3 -12
- package/dist/upgrade-cli.js +22 -76
- package/dist/upgrade-marker-parse.d.ts +32 -0
- package/dist/upgrade-marker-parse.js +73 -0
- package/package.json +9 -6
- package/src/changes.json +245 -0
- package/src/scripts/codemod/codemod-support.ts +1 -1
- package/src/scripts/codemod/db-factory.ts +1 -1
- package/dist/engine/provider-plugin-context.d.ts +0 -3
- package/dist/engine/provider-plugin-context.js +0 -28
|
@@ -40,6 +40,7 @@ export type ManifestFeature = {
|
|
|
40
40
|
readonly name: string;
|
|
41
41
|
readonly description: string | null;
|
|
42
42
|
readonly toggleableDefault: boolean | null;
|
|
43
|
+
readonly adoptsBundledLists?: true;
|
|
43
44
|
readonly requires: readonly string[];
|
|
44
45
|
readonly optionalRequires: readonly string[];
|
|
45
46
|
readonly configReads: readonly string[];
|
|
@@ -59,6 +59,7 @@ export function buildManifestFromRegistry(registry, options) {
|
|
|
59
59
|
name: feature.name,
|
|
60
60
|
description: feature.description ?? null,
|
|
61
61
|
toggleableDefault: feature.toggleableDefault ?? null,
|
|
62
|
+
...(feature.adoptsBundledLists && { adoptsBundledLists: true }),
|
|
62
63
|
requires: [...feature.requires],
|
|
63
64
|
optionalRequires: [...feature.optionalRequires],
|
|
64
65
|
configReads: [...feature.configReads],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EntityDefinition, SessionUser } from "./types/index.ts";
|
|
2
2
|
export declare function maskWriteOnlyFields<T extends Readonly<Record<string, unknown>>>(entity: EntityDefinition, row: T): T;
|
|
3
3
|
export declare function dropEmptyWriteOnlyValues<T extends Readonly<Record<string, unknown>>>(entity: EntityDefinition, values: T): T;
|
|
4
|
+
export declare function userMayReadFieldInBulk(entity: EntityDefinition, fieldName: string, user: Pick<SessionUser, "roles">): boolean;
|
|
4
5
|
export declare function filterReadFields(entity: EntityDefinition, data: Readonly<Record<string, unknown>>, user: SessionUser): Record<string, unknown>;
|
|
5
6
|
export declare function checkWriteFieldRoles(entity: EntityDefinition, changes: Readonly<Record<string, unknown>>, user: SessionUser): string | null;
|
|
6
7
|
export declare function checkWriteFieldOwnership(entity: EntityDefinition, submittedChanges: Readonly<Record<string, unknown>>, user: SessionUser, oldRow?: Readonly<Record<string, unknown>>, rowContext?: Readonly<Record<string, unknown>>): string | null;
|
|
@@ -35,6 +35,18 @@ export function dropEmptyWriteOnlyValues(entity, values) {
|
|
|
35
35
|
delete kept[name];
|
|
36
36
|
return kept; // @cast-boundary engine-payload
|
|
37
37
|
}
|
|
38
|
+
// Role-level gate for bulk exports: false for writeOnly fields and for fields whose read-access
|
|
39
|
+
// map grants none of the user's roles. Per-row ownership rules are not evaluated here; those
|
|
40
|
+
// cells come back empty from the list query itself.
|
|
41
|
+
export function userMayReadFieldInBulk(entity, fieldName, user) {
|
|
42
|
+
const field = entity.fields[fieldName];
|
|
43
|
+
if (!field)
|
|
44
|
+
return false;
|
|
45
|
+
if (field.type === "text" && field.writeOnly === true)
|
|
46
|
+
return false;
|
|
47
|
+
const accessMap = normalizeAccessEntry(field.access?.read);
|
|
48
|
+
return accessMap === undefined || user.roles.some((role) => accessMap[role] !== undefined);
|
|
49
|
+
}
|
|
38
50
|
// Field-level read filtering. Returns a copy of `data` with fields stripped
|
|
39
51
|
// if the user's roles don't grant read access OR the ownership-rule for the
|
|
40
52
|
// matching role doesn't accept this concrete row. Fields without access
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { handlerFieldLabelKey, handlerTitleKey } from "../i18n/required-surface-keys.ts";
|
|
2
2
|
export { hasAccess } from "./access.ts";
|
|
3
3
|
export { isPrincipalStatusPlugin, isTenantLifecycleStatusPlugin, type PrincipalProfile, type PrincipalStatus, type PrincipalStatusPlugin, TENANT_TEARDOWN_STATUSES, type TenantLifecycleStatusPlugin, } from "./active-membership.ts";
|
|
4
|
+
export { validateBundledListsOptions } from "./boot-validator/bundled-lists-table.ts";
|
|
4
5
|
export { PII_DIRECT_NAME_HINTS, PII_USER_OWNED_NAME_HINTS, PII_USER_REFERENCE_NAME_HINTS, } from "./boot-validator/entity-handler.ts";
|
|
5
6
|
export { entityHasAnonymizableSubjectField } from "./boot-validator/pii-retention.ts";
|
|
6
7
|
export { type CardMetaOverflow, collectCardMetaOverflow, collectWriteHandlerQns, MAX_TRANSFER_DEPTH, SECURITY_BASELINE_FEATURE_NAMES, type ValidateBootOptions, validateAppCustomScreenWriteQns, validateBoot, } from "./boot-validator.ts";
|
|
@@ -59,7 +60,6 @@ export { parseRefTarget, parseRefTargetEntityName } from "./parse-ref-target.ts"
|
|
|
59
60
|
export { buildPipelineSteps, stepsPipeline } from "./pipeline.ts";
|
|
60
61
|
export { projectAppSchemaForRoles } from "./project-app-schema-for-roles.ts";
|
|
61
62
|
export { defineApply, defineMspApply, setFields } from "./projection-helpers.ts";
|
|
62
|
-
export { providerPluginContextFromHandler, providerPluginContextFromJob, } from "./provider-plugin-context.ts";
|
|
63
63
|
export type { BuiltinQnType, ParsedQn, QnType } from "./qualified-name.ts";
|
|
64
64
|
export { isValidQn, parseQn, QnTypes, qn, toKebab } from "./qualified-name.ts";
|
|
65
65
|
export { readClaim } from "./read-claim.ts";
|
|
@@ -87,7 +87,7 @@ export { assertNotificationVariables, templateVariable, templateVariablesToZod,
|
|
|
87
87
|
export { type EffectiveFeaturesResolver, findTierResolverUsage, isTierResolverPlugin, requireTierResolverPlugin, TENANT_TIER_RESOLVER_EXT, type TierResolverPlugin, type TrialGate, } from "./tier-resolver-extension.ts";
|
|
88
88
|
export { findOverDeepTransferChain, resolveTransferAdjacency as resolveEntityTransferAdjacency, type TransferAdjacency, type TransferEdge, type TransferEdgeLink, } from "./transfer-adjacency.ts";
|
|
89
89
|
export { isSystemTenant, isUuid, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers.ts";
|
|
90
|
-
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BeforeTransactionPhase, BeforeTransactionResult, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, BootOptionName, CamelToKebab, ChatSendFailureCode, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, ConcurrencyMode, ConfigAccessor, ConfigAccessorFactory, ConfigBacking, ConfigCascade, ConfigCascadeLevel, ConfigDefinition, ConfigEditScreenDefinition, ConfigKeyAccess, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigMask, ConfigResolver, ConfigScope, ConfigSecretsReader, ConfigSeedDef, ConfigStoredRow, ConfigStoredRowWithSource, ConfigValidateContext, ConfigValidateFn, ConfigValue, ConfigValueSource, ConfigValueWithSource, ContentCollectionDefinition, CreateSeedOptions, CreateTenantSeedOptions, CreateUserSeedOptions, CustomScreenDefinition, CustomScreenRoute, DashboardChartPanel, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, DashboardTextParam, DashboardValueFormat, DateFieldDef, DecimalFieldDef, DeleteContext, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EmbeddedListEditSpec, EntityDefinition, EntityEditAction, EntityEditActionPlacement, EntityEditScreenDefinition, EntityId, EntityListExpandableRow, EntityListScreenDefinition, EntityObjectDefinition, EntityRef, EntityRelations, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, ExtensionOptionsArgs, ExtensionOptionsFor, FeatureDefinition, FeatureRegistrar, FieldAccess, FieldCondition, FieldDefinition, FieldFormatRegistry, FieldIconKey, FieldRenderer, FileFieldDef, FilesFieldDef, Findability, FormatSpec, FormWidth, HandlerContext, HasManyRelation, HookDefinition, HookMap, ImageFieldDef, ImagesFieldDef, JobBackoff, JobBackoffStrategy, JobContext, JobDeferral, JobDefinition, JobHandlerFn, JobTrigger, JobTriggerEvent, JsonbFieldDef, KumikoEntityTypeMap, KumikoEventTypeMap, KumikoExtensionOptionsMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, LifecycleHookType, ListColumnSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, LongTextFindability, ManyToManyRelation, MaxPerTenantOptions, MemberReader, MetricNavigate, MetricSpec, MspErrorMode, MspErrorPolicy, MultiSelectFieldDef, MultiStreamApplyFn, MultiStreamProjectionDefinition, NameOrRef, NavDefinition, NavIconKey, NotificationDataFn, NotificationDefinition, NotificationLocalizedTemplate, NotificationRecipientFn, NotificationRecordMapping, NotificationTemplateBody, NotificationTemplateFn, NotificationTemplateMeta, NotifyDeferred, NotifyDelivery, NotifyDeliveryStatus, NotifyDispatched, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, NumberFieldDef, OnDeleteStrategy, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, ParentRefDef, PersonalAnnotations, PersonalAnnotationsLongText, PersonalAnnotationsNoFind, PersonalSubject, PlatformComponent, PluralForms, PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreparedWriteEvent, PreQueryHookFn, PreSaveHookFn, ProjectionDefinition, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, ProjectionTable,
|
|
90
|
+
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BeforeTransactionPhase, BeforeTransactionResult, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, BootOptionName, BundledListsOptions, CamelToKebab, ChatSendFailureCode, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, ConcurrencyMode, ConfigAccessor, ConfigAccessorFactory, ConfigBacking, ConfigCascade, ConfigCascadeLevel, ConfigDefinition, ConfigEditScreenDefinition, ConfigKeyAccess, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigMask, ConfigResolver, ConfigScope, ConfigSecretsReader, ConfigSeedDef, ConfigStoredRow, ConfigStoredRowWithSource, ConfigValidateContext, ConfigValidateFn, ConfigValue, ConfigValueSource, ConfigValueWithSource, ContentCollectionDefinition, CreateSeedOptions, CreateTenantSeedOptions, CreateUserSeedOptions, CustomScreenDefinition, CustomScreenRoute, DashboardChartPanel, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, DashboardTextParam, DashboardValueFormat, DateFieldDef, DecimalFieldDef, DeleteContext, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EmbeddedListEditSpec, EntityDefinition, EntityEditAction, EntityEditActionPlacement, EntityEditScreenDefinition, EntityId, EntityListExpandableRow, EntityListScreenDefinition, EntityObjectDefinition, EntityRef, EntityRelations, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, ExpandableRowGrouping, ExtensionOptionsArgs, ExtensionOptionsFor, FeatureDefinition, FeatureRegistrar, FieldAccess, FieldCondition, FieldDefinition, FieldFormatRegistry, FieldIconKey, FieldRenderer, FileFieldDef, FilesFieldDef, Findability, FormatSpec, FormWidth, HandlerContext, HasManyRelation, HookDefinition, HookMap, ImageFieldDef, ImagesFieldDef, JobBackoff, JobBackoffStrategy, JobContext, JobDeferral, JobDefinition, JobHandlerFn, JobTrigger, JobTriggerEvent, JsonbFieldDef, KumikoEntityTypeMap, KumikoEventTypeMap, KumikoExtensionOptionsMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, LifecycleHookType, ListColumnSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, ListTableOptions, LongTextFindability, ManyToManyRelation, MaxPerTenantOptions, MemberReader, MetricNavigate, MetricSpec, MspErrorMode, MspErrorPolicy, MultiSelectFieldDef, MultiStreamApplyFn, MultiStreamProjectionDefinition, NameOrRef, NavDefinition, NavIconKey, NotificationDataFn, NotificationDefinition, NotificationLocalizedTemplate, NotificationRecipientFn, NotificationRecordMapping, NotificationTemplateBody, NotificationTemplateFn, NotificationTemplateMeta, NotifyDeferred, NotifyDelivery, NotifyDeliveryStatus, NotifyDispatched, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, NumberFieldDef, OnDeleteStrategy, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, ParentRefDef, PersonalAnnotations, PersonalAnnotationsLongText, PersonalAnnotationsNoFind, PersonalSubject, PlatformComponent, PluralForms, PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreparedWriteEvent, PreQueryHookFn, PreSaveHookFn, ProjectionDefinition, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, ProjectionTable, QualifiedEventName, QueryEvent, QueryHandlerDef, QueryHandlerFn, RateLimitPer, RecordHeaderSpec, RecordHeaderSubtitlePart, ReferenceDataDef, Registry, RelationDefinition, ResolvedPiiFlags, RetentionDef, RoleAccessPersonalData, RoleAccessRule, RowAction, RowActionNavigate, RowActionNavigateBase, RowActionWriteHandler, RowFieldExtractor, SaveContext, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenNavSugar, ScreenSlots, SecretKeyDefinition, SecretKeyHandle, SecretMintConfirmStep, SecretMintScreenDefinition, SecretNamespaceDefinition, SecretNamespaceHandle, SecretNamespaceOptions, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, SelectFieldDef, SessionUser, SessionUserOrigin, StackBootRequirement, StreamHandlerDef, StreamHandlerFn, Subscribe, TargetRef, TemplateImagesVariableDef, TemplateListVariableDef, TemplateScalarVariableDef, TemplateVariableDef, TemplateVariables, TenantId, TenantWriteTarget, TextFieldDef, ToolbarAction, TranslationEntry, TranslationKeys, TranslationsDef, TranslationValue, TreeAction, TreeActionDef, TreeActionsHandle, TreeChildrenSubscribe, TreeNode, TreeNodeState, UnitKey, UnsafeAppendEventFn, ValidationError, ValidationHookFn, WorkspaceDefinition, WriteEvent, WriteHandlerDef, WriteHandlerFn, WriteResult, } from "./types/index.ts";
|
|
91
91
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HookPhases, HTTP_ERROR_CODE_PATTERN, isAgentVisibleScreen, isDeliveryErrorCode, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.ts";
|
|
92
92
|
export type { AwaitedEventType, PipelineBuildCtx, PipelineCtx, PipelineDef, StepBuilder, StepDef, StepFailureStrategy, StepInstance, StepKind, StepNamespace, StepResolver, } from "./types/step.ts";
|
|
93
93
|
export { runValidation } from "./validation.ts";
|
package/dist/engine/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export { handlerFieldLabelKey, handlerTitleKey } from "../i18n/required-surface-keys.js";
|
|
3
3
|
export { hasAccess } from "./access.js";
|
|
4
4
|
export { isPrincipalStatusPlugin, isTenantLifecycleStatusPlugin, TENANT_TEARDOWN_STATUSES, } from "./active-membership.js";
|
|
5
|
+
export { validateBundledListsOptions } from "./boot-validator/bundled-lists-table.js";
|
|
5
6
|
export { PII_DIRECT_NAME_HINTS, PII_USER_OWNED_NAME_HINTS, PII_USER_REFERENCE_NAME_HINTS, } from "./boot-validator/entity-handler.js";
|
|
6
7
|
export { entityHasAnonymizableSubjectField } from "./boot-validator/pii-retention.js";
|
|
7
8
|
export { collectCardMetaOverflow, collectWriteHandlerQns, MAX_TRANSFER_DEPTH, SECURITY_BASELINE_FEATURE_NAMES, validateAppCustomScreenWriteQns, validateBoot, } from "./boot-validator.js";
|
|
@@ -56,7 +57,6 @@ export { parseRefTarget, parseRefTargetEntityName } from "./parse-ref-target.js"
|
|
|
56
57
|
export { buildPipelineSteps, stepsPipeline } from "./pipeline.js";
|
|
57
58
|
export { projectAppSchemaForRoles } from "./project-app-schema-for-roles.js";
|
|
58
59
|
export { defineApply, defineMspApply, setFields } from "./projection-helpers.js";
|
|
59
|
-
export { providerPluginContextFromHandler, providerPluginContextFromJob, } from "./provider-plugin-context.js";
|
|
60
60
|
export { isValidQn, parseQn, QnTypes, qn, toKebab } from "./qualified-name.js";
|
|
61
61
|
export { readClaim } from "./read-claim.js";
|
|
62
62
|
export { createRegistry } from "./registry.js";
|
|
@@ -14,12 +14,13 @@
|
|
|
14
14
|
// the Designer/LLM relies on.
|
|
15
15
|
import { aiClassifySchema, aiExtractSchema, aiGenerateSchema, authClaimsSchema, defineEventSchema, hookSchema, httpRouteSchema, jobSchema, multiStreamProjectionSchema, notificationSchema, projectionSchema, queryHandlerSchema, screenSchema, streamHandlerSchema, writeHandlerSchema, } from "./mixed-schemas.js";
|
|
16
16
|
import { envSchemaSchema, exposesApiSchema, extendsRegistrarSchema, treeActionsSchema, unknownSchema, usesApiSchema, } from "./opaque-schemas.js";
|
|
17
|
-
import { claimKeySchema, configSchema, describeSchema, entitySchema, metricSchema, navSchema, optionalRequiresSchema, readsConfigSchema, referenceDataSchema, relationSchema, requiresSchema, secretNamespaceSchema, secretSchema, systemScopeSchema, toggleableSchema, translationsSchema, uiHintsSchema, useExtensionSchema, workspaceSchema, } from "./static-schemas.js";
|
|
17
|
+
import { adoptsBundledListsSchema, claimKeySchema, configSchema, describeSchema, entitySchema, metricSchema, navSchema, optionalRequiresSchema, readsConfigSchema, referenceDataSchema, relationSchema, requiresSchema, secretNamespaceSchema, secretSchema, systemScopeSchema, toggleableSchema, translationsSchema, uiHintsSchema, useExtensionSchema, workspaceSchema, } from "./static-schemas.js";
|
|
18
18
|
export const PATTERN_LIBRARY = {
|
|
19
19
|
requires: requiresSchema,
|
|
20
20
|
optionalRequires: optionalRequiresSchema,
|
|
21
21
|
readsConfig: readsConfigSchema,
|
|
22
22
|
systemScope: systemScopeSchema,
|
|
23
|
+
adoptsBundledLists: adoptsBundledListsSchema,
|
|
23
24
|
toggleable: toggleableSchema,
|
|
24
25
|
describe: describeSchema,
|
|
25
26
|
uiHints: uiHintsSchema,
|
|
@@ -3,6 +3,7 @@ export declare const requiresSchema: PatternFormSchema;
|
|
|
3
3
|
export declare const optionalRequiresSchema: PatternFormSchema;
|
|
4
4
|
export declare const readsConfigSchema: PatternFormSchema;
|
|
5
5
|
export declare const systemScopeSchema: PatternFormSchema;
|
|
6
|
+
export declare const adoptsBundledListsSchema: PatternFormSchema;
|
|
6
7
|
export declare const toggleableSchema: PatternFormSchema;
|
|
7
8
|
export declare const describeSchema: PatternFormSchema;
|
|
8
9
|
export declare const uiHintsSchema: PatternFormSchema;
|
|
@@ -59,6 +59,15 @@ export const systemScopeSchema = {
|
|
|
59
59
|
singleton: true,
|
|
60
60
|
fields: [],
|
|
61
61
|
};
|
|
62
|
+
export const adoptsBundledListsSchema = {
|
|
63
|
+
kind: "adoptsBundledLists",
|
|
64
|
+
label: { en: "Adopts bundled lists", de: "Übernimmt Bundled-Listen" },
|
|
65
|
+
summary: { en: "Feature packages only: takes the app-wide bundledLists table block." },
|
|
66
|
+
category: "meta",
|
|
67
|
+
editability: "opaque",
|
|
68
|
+
singleton: true,
|
|
69
|
+
fields: [],
|
|
70
|
+
};
|
|
62
71
|
export const toggleableSchema = {
|
|
63
72
|
kind: "toggleable",
|
|
64
73
|
label: { en: "Toggleable", de: "Umschaltbar" },
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// pointing at a missing nav; that is pruned here.
|
|
14
14
|
import { createFallbackLogger, scrubLogText } from "../logging/index.js";
|
|
15
15
|
import { resolveScreenTargetQn } from "./boot-validator/screens.js";
|
|
16
|
+
import { workspaceOwnerOf } from "./build-app-schema.js";
|
|
16
17
|
import { isToggleableFeature } from "./effective-features.js";
|
|
17
18
|
import { isPureGroupingNav } from "./project-app-schema-for-roles.js";
|
|
18
19
|
import { isValidQn, qualifyEntityName } from "./qualified-name.js";
|
|
@@ -141,10 +142,13 @@ export function stripDisabledFeaturesFromAppSchema(schema, registry, disabledFea
|
|
|
141
142
|
const { navs: _navs, ...rest } = feature;
|
|
142
143
|
return navs.length > 0 ? { ...rest, navs } : rest;
|
|
143
144
|
});
|
|
144
|
-
// Workspace
|
|
145
|
-
//
|
|
145
|
+
// Workspace ids are unique per feature only, so the owner decides. A definition the build did
|
|
146
|
+
// not register (schema assembled elsewhere) falls back to the short id.
|
|
146
147
|
const disabledWorkspaceIds = new Set(disabledFeatureNames.flatMap((featureName) => Object.keys(registry.features.get(featureName)?.workspaces ?? {})));
|
|
147
|
-
const workspaces = schema.workspaces?.filter((ws) =>
|
|
148
|
+
const workspaces = schema.workspaces?.filter((ws) => {
|
|
149
|
+
const owner = workspaceOwnerOf(ws.definition);
|
|
150
|
+
return owner === undefined ? !disabledWorkspaceIds.has(ws.definition.id) : !disabled.has(owner);
|
|
151
|
+
});
|
|
148
152
|
const { workspaces: _original, ...restSchema } = schema;
|
|
149
153
|
return {
|
|
150
154
|
...restSchema,
|
|
@@ -4,10 +4,11 @@ export type { QueryHandlerDefinition, StreamHandlerDefinition, WriteHandlerDefin
|
|
|
4
4
|
export type { EntityCrudRegistrar, EntityCrudVerb, EntityHandlerOptions, EntityQueryHandlerOptions, EntityWriteHandlerOptions, RegisterEntityCrudOptions, } from "@cosmicdrift/kumiko-types/entity-handlers";
|
|
5
5
|
export type { KumikoEntityTypeMap, KumikoEventTypeMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, } from "@cosmicdrift/kumiko-types/event-type-map";
|
|
6
6
|
export type { ExtensionOptionsArgs, ExtensionOptionsFor, KumikoExtensionOptionsMap, } from "@cosmicdrift/kumiko-types/extension-options-map";
|
|
7
|
-
export type { BootCheckContext, BootCheckFn, BootOptionName,
|
|
7
|
+
export type { BootCheckContext, BootCheckFn, BootOptionName, FeatureDefinition, FeatureMetricDef, FeatureMetricType, MetricOptions, Registry, SecretKeyDefinition, SecretKeyHandle, SecretNamespaceDefinition, SecretNamespaceHandle, SecretNamespaceOptions, SecretOptions, StackBootRequirement, StoreTableDef, StoreTableEntry, StoreTableOptions, UiHintOption, UiHints, } from "@cosmicdrift/kumiko-types/feature";
|
|
8
|
+
export type { EntityObjectDefinition, FeatureRegistrar, HookDefinition, } from "@cosmicdrift/kumiko-types/feature-registrar";
|
|
8
9
|
export type { FieldIconKey } from "@cosmicdrift/kumiko-types/field-icon";
|
|
9
10
|
export type { AnyFileFieldDef, BigIntFieldDef, BooleanFieldDef, ClientDerivedFieldDef, DateFieldDef, DecimalFieldDef, DefaultCurrency, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EmbeddedDerivedCellDef, EmbeddedFieldDef, EmbeddedSubFieldDef, EntityDefinition, EntityEventMigration, EntityFieldDefinition, EntityIndexDef, EntityLongTextFieldDef, EntityTextFieldDef, FieldAccess, FieldDefinition, FieldsMap, FileFieldDef, FilesFieldDef, Findability, ImageFieldDef, ImagesFieldDef, JsonbFieldDef, LocatedTimestampFieldDef, LongTextFieldDef, LongTextFindability, MoneyCurrencySource, MoneyFieldDef, MultiSelectFieldDef, NumberFieldDef, OptionsQueryPayload, OptionsQueryPayloadValue, ParentRefDef, PersonalAnnotations, PersonalAnnotationsLongText, PersonalAnnotationsNoFind, PersonalSubject, ReferenceFieldDef, ResolvedPiiFlags, ResolvedPiiStance, RetentionDef, SelectFieldDef, SelectOptionTone, TextFieldDef, TimestampFieldDef, TransitionMap, TzFieldDef, } from "@cosmicdrift/kumiko-types/fields";
|
|
10
|
-
export type { AccessRule, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AggregateStreamHandle, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BeforeTransactionPhase, BeforeTransactionResult, CamelToKebab, ChatSendFailureCode, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, DeclarativeEventMigration, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DispatchWriteRef, EntityRef, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, EventMigrationDef, EventPiiFields, EventPiiStance, EventUpcastCtx, EventUpcastFn, FetchForWritingArgs, HandlerContext, HandlerRef, JobContext, JobDeferral, JobRunnerRef, JobTriggerEvent, MemberReader, NameOrRef, NotifyDeferred, NotifyDelivery, NotifyDeliveryStatus, NotifyDispatched, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, PayloadBucketNormalization, PayloadRateLimitOption, PreparedWriteEvent,
|
|
11
|
+
export type { AccessRule, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AggregateStreamHandle, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BeforeTransactionPhase, BeforeTransactionResult, CamelToKebab, ChatSendFailureCode, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, DeclarativeEventMigration, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DispatchWriteRef, EntityRef, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, EventMigrationDef, EventPiiFields, EventPiiStance, EventUpcastCtx, EventUpcastFn, FetchForWritingArgs, HandlerContext, HandlerRef, JobContext, JobDeferral, JobRunnerRef, JobTriggerEvent, MemberReader, NameOrRef, NotifyDeferred, NotifyDelivery, NotifyDeliveryStatus, NotifyDispatched, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, PayloadBucketNormalization, PayloadRateLimitOption, PreparedWriteEvent, QualifiedEventName, QueryEvent, QueryHandlerDef, QueryHandlerFn, RateLimitDeclaration, RateLimitDisabled, RateLimitOption, RateLimitOptions, RateLimitPer, RoleAccessPersonalData, RoleAccessRule, SessionUser, SessionUserOrigin, StreamHandlerDef, StreamHandlerFn, TenantWriteTarget, TryAppendEventFn, TryAppendEventResult, UnsafeAppendEventFn, WriteEvent, WriteHandlerDef, WriteHandlerFn, WriteResult, } from "@cosmicdrift/kumiko-types/handlers";
|
|
11
12
|
export { DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, declaredRateLimitOptions, HTTP_ERROR_CODE_PATTERN, isDeliveryErrorCode, isOpenToAllGranted, isRateLimitDisabled, isRateLimitOption, isUiAccessGranted, } from "@cosmicdrift/kumiko-types/handlers";
|
|
12
13
|
export type { DeleteContext, EntityHookMap, HookMap, HookOwner, HookPhase, LifecycleHookFn, LifecycleResult, OwnedFn, PhasedHook, PostDeleteBatchHookFn, PostDeleteHookFn, PostQueryHookFn, PostSaveBatchHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, SaveContext, SearchPayloadContributorFn, ValidationError, ValidationHookFn, } from "@cosmicdrift/kumiko-types/hooks";
|
|
13
14
|
export type { HttpRouteDefinition, HttpRouteHandler, HttpRouteHandlerDeps, HttpRouteMethod, } from "@cosmicdrift/kumiko-types/http-route";
|
|
@@ -17,7 +18,7 @@ export type { ContentCollectionDefinition, NavDefinition, NavIconKey, } from "@c
|
|
|
17
18
|
export type { FromRule, FromRuleKind, OwnershipClause, OwnershipMap, OwnershipRef, OwnershipRule, SqlFragment, WhereRule, WhereRuleContext, } from "@cosmicdrift/kumiko-types/ownership";
|
|
18
19
|
export type { EntityProjectionExtension, MspErrorMode, MspErrorPolicy, MultiStreamApplyFn, MultiStreamProjectionDefinition, ProjectionDefinition, ProjectionRowIdOf, ProjectionTable, SingleStreamApplyFn, } from "@cosmicdrift/kumiko-types/projection";
|
|
19
20
|
export type { BelongsToRelation, EntityRelations, HasManyRelation, ManyToManyRelation, RelationDefinition, } from "@cosmicdrift/kumiko-types/relations";
|
|
20
|
-
export type { ActionFormScreenDefinition, ConfigEditScreenDefinition, CustomScreenDefinition, CustomScreenRoute, DashboardChartMarkerKind, DashboardChartPanel, DashboardChartRangeOption, DashboardChartRanges, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelGate, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, DashboardTextParam, DashboardValueFormat, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EditWriteFormSection, EmbeddedListEditSpec, EntityEditAction, EntityEditActionPlacement, EntityEditScreenDefinition, EntityListExpandableRow, EntityListScreenDefinition, FieldCondition, FieldFormatRegistry, FieldRenderer, FormatSpec, FormFieldDerivation, FormWidth, ListColumnSpec, ListEmptyStateSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, MetricNavigate, MetricSpec, PlatformComponent, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, RecordHeaderSpec, RecordHeaderSubtitlePart, RowAction, RowActionNavigate, RowActionNavigateBase, RowActionWriteHandler, RowFieldExtractor, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenNavSugar, ScreenSlots, SecretMintConfirmStep, SecretMintScreenDefinition, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, ToolbarAction, UnitKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
21
|
+
export type { ActionFormScreenDefinition, BundledListsOptions, ConfigEditScreenDefinition, CustomScreenDefinition, CustomScreenRoute, DashboardChartMarkerKind, DashboardChartPanel, DashboardChartRangeOption, DashboardChartRanges, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelGate, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, DashboardTextParam, DashboardValueFormat, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EditWriteFormSection, EmbeddedListEditSpec, EntityEditAction, EntityEditActionPlacement, EntityEditScreenDefinition, EntityListExpandableRow, EntityListScreenDefinition, ExpandableRowGrouping, FieldCondition, FieldFormatRegistry, FieldRenderer, FormatSpec, FormFieldDerivation, FormWidth, ListColumnSpec, ListEmptyStateSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, ListTableOptions, MetricNavigate, MetricSpec, PlatformComponent, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, RecordHeaderSpec, RecordHeaderSubtitlePart, RowAction, RowActionNavigate, RowActionNavigateBase, RowActionWriteHandler, RowFieldExtractor, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenNavSugar, ScreenSlots, SecretMintConfirmStep, SecretMintScreenDefinition, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, ToolbarAction, UnitKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
21
22
|
export { relatedListGroupHeaderLabel, relatedListGroupKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
22
23
|
export type { TargetRef } from "@cosmicdrift/kumiko-types/target-ref";
|
|
23
24
|
export type { Subscribe, TreeAction, TreeActionDef, TreeActionsHandle, TreeChildrenSubscribe, TreeNode, TreeNodeState, } from "@cosmicdrift/kumiko-types/tree-node";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export type * from "@cosmicdrift/kumiko-types/screen";
|
|
2
|
-
export { relatedListGroupHeaderLabel, relatedListGroupKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
2
|
+
export { DEFAULT_EXPORT_MAX_ROWS, LIST_BULK_ACTION_KEYS, LIST_COLUMN_PINS, LIST_TABLE_DENSITIES, LIST_TABLE_DENSITY_KEYS, LIST_TABLE_EXPORT_KEYS, LIST_TABLE_GROUPING_KEYS, LIST_TABLE_OPTION_KEYS, LIST_TABLE_TOTAL_KEYS, MAX_BULK_SELECTION, MAX_EXPORT_MAX_ROWS, relatedListGroupHeaderLabel, relatedListGroupKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { relatedListGroupHeaderLabel, relatedListGroupKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
1
|
+
export { DEFAULT_EXPORT_MAX_ROWS, LIST_BULK_ACTION_KEYS, LIST_COLUMN_PINS, LIST_TABLE_DENSITIES, LIST_TABLE_DENSITY_KEYS, LIST_TABLE_EXPORT_KEYS, LIST_TABLE_GROUPING_KEYS, LIST_TABLE_OPTION_KEYS, LIST_TABLE_TOTAL_KEYS, MAX_BULK_SELECTION, MAX_EXPORT_MAX_ROWS, relatedListGroupHeaderLabel, relatedListGroupKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
@@ -63,6 +63,7 @@ export type ApiEntrypoint = {
|
|
|
63
63
|
readonly lifecycle: Lifecycle;
|
|
64
64
|
readonly observability: ObservabilityProvider;
|
|
65
65
|
readonly dispatcher: Dispatcher;
|
|
66
|
+
readonly context: AppContext;
|
|
66
67
|
readonly lifecycleHooks: LifecycleHooks;
|
|
67
68
|
readonly eventDispatcher?: EventDispatcher;
|
|
68
69
|
readonly mode: "api";
|
|
@@ -74,6 +75,7 @@ export type WorkerEntrypoint = {
|
|
|
74
75
|
readonly eventDispatcher: EventDispatcher;
|
|
75
76
|
readonly jobRunner: JobRunner;
|
|
76
77
|
readonly observability: ObservabilityProvider;
|
|
78
|
+
readonly context: AppContext;
|
|
77
79
|
readonly dispatcher: Dispatcher;
|
|
78
80
|
readonly mode: "worker";
|
|
79
81
|
start(): Promise<void>;
|
package/dist/entrypoint/index.js
CHANGED
|
@@ -244,6 +244,7 @@ export function createApiEntrypoint(options) {
|
|
|
244
244
|
lifecycle,
|
|
245
245
|
observability: server.observability,
|
|
246
246
|
dispatcher: server.dispatcher,
|
|
247
|
+
context: server.context,
|
|
247
248
|
lifecycleHooks: server.lifecycleHooks,
|
|
248
249
|
...(server.eventDispatcher && { eventDispatcher: server.eventDispatcher }),
|
|
249
250
|
mode: "api",
|
|
@@ -279,6 +280,7 @@ export function createWorkerEntrypoint(options) {
|
|
|
279
280
|
jobRunner,
|
|
280
281
|
observability: server.observability,
|
|
281
282
|
dispatcher: server.dispatcher,
|
|
283
|
+
context: server.context,
|
|
282
284
|
mode: "worker",
|
|
283
285
|
async start() {
|
|
284
286
|
await eventDispatcher.start();
|
|
@@ -324,6 +326,7 @@ export function createAllInOneEntrypoint(options) {
|
|
|
324
326
|
jobRunner: workerJobRunner,
|
|
325
327
|
observability: server.observability,
|
|
326
328
|
dispatcher: server.dispatcher,
|
|
329
|
+
context: server.context,
|
|
327
330
|
lifecycleHooks: server.lifecycleHooks,
|
|
328
331
|
mode: "all-in-one",
|
|
329
332
|
async start() {
|
|
@@ -19,6 +19,7 @@ export declare function append(db: DbRunner, event: EventToAppend): Promise<Stor
|
|
|
19
19
|
export declare function loadAggregate(db: DbRunner, aggregateId: string, tenantId: TenantId, options?: {
|
|
20
20
|
readonly includeArchived?: boolean;
|
|
21
21
|
readonly aggregateType?: string;
|
|
22
|
+
readonly atVersion?: number;
|
|
22
23
|
}): Promise<readonly StoredEvent[]>;
|
|
23
24
|
export declare function loadAggregateForWriting(db: DbRunner, aggregateId: string, tenantId: TenantId, options: {
|
|
24
25
|
readonly aggregateType: string;
|
|
@@ -213,9 +213,12 @@ export async function loadAggregate(db, aggregateId, tenantId, options) {
|
|
|
213
213
|
if (archived)
|
|
214
214
|
return [];
|
|
215
215
|
}
|
|
216
|
-
const rows = await selectMany(db, eventsTable,
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
const rows = await selectMany(db, eventsTable, {
|
|
217
|
+
aggregateId,
|
|
218
|
+
tenantId,
|
|
219
|
+
...(options?.aggregateType !== undefined && { aggregateType: options.aggregateType }),
|
|
220
|
+
...(options?.atVersion !== undefined && { version: options.atVersion }),
|
|
221
|
+
}, { orderBy: { col: "version", direction: "asc" } });
|
|
219
222
|
return rows.map(toStoredEvent);
|
|
220
223
|
}
|
|
221
224
|
// Events and the stream's latest version from ONE read, so both come from the same
|
|
@@ -311,7 +314,7 @@ export async function loadEventsAfterVersion(db, aggregateId, tenantId, afterVer
|
|
|
311
314
|
// abort iteration. Checked both at batch boundaries (before the next
|
|
312
315
|
// fetch) AND between yields (so abort lands within one event regardless
|
|
313
316
|
// of batch size). Throws AbortError on the first check after abort;
|
|
314
|
-
// in-flight queries are not actively cancelled (
|
|
317
|
+
// in-flight queries are not actively cancelled (connection
|
|
315
318
|
// cancel is a separate, riskier concern handled per-query at the TenantDb
|
|
316
319
|
// boundary).
|
|
317
320
|
export async function* streamAllEventsByType(db, aggregateType, batchSize = 1000, signal) {
|
|
@@ -32,7 +32,7 @@ export async function appendProvenanceEvent(db, event) {
|
|
|
32
32
|
message: `appendProvenanceEvent: event tenant "${event.tenantId}" does not match the TenantDb's tenant "${db.tenantId}".`,
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
//
|
|
35
|
+
// Postgres aborts the whole surrounding transaction on a statement
|
|
36
36
|
// error (25P02) even when the caller's catch swallows it — the savepoint
|
|
37
37
|
// confines that to this scope. Pool connections have no ambient tx to poison.
|
|
38
38
|
await runInSavepointIfSupported(runner, async (sp) => {
|
|
@@ -46,6 +46,9 @@ export async function createFileRefFromBytes(ctx, input) {
|
|
|
46
46
|
return fileRejectedFailure(outcome.error);
|
|
47
47
|
case "failed":
|
|
48
48
|
return outcome.failure;
|
|
49
|
+
case "target-locked":
|
|
50
|
+
// Only the HTTP route supplies the lock re-check, so this path cannot produce it.
|
|
51
|
+
return writeFailure(new InternalError({ message: "createFileRefFromBytes: unexpected target-locked outcome" }));
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
54
|
function fileRejectedFailure(error) {
|
|
@@ -50,6 +50,7 @@ export type StoreFileDeps = {
|
|
|
50
50
|
readonly defaultMaxUploadSize: string;
|
|
51
51
|
readonly writeBytes: (storageKey: string, data: Uint8Array, mimeType: string) => Promise<void>;
|
|
52
52
|
readonly deleteBytes: (storageKey: string) => Promise<void>;
|
|
53
|
+
readonly isAttachTargetLocked?: (rowDb: TenantDb) => Promise<boolean>;
|
|
53
54
|
};
|
|
54
55
|
export type CreatedFileRef = {
|
|
55
56
|
readonly id: string;
|
|
@@ -80,6 +81,8 @@ export type StoreFileOutcome = {
|
|
|
80
81
|
} | {
|
|
81
82
|
readonly kind: "rejected";
|
|
82
83
|
readonly error: string;
|
|
84
|
+
} | {
|
|
85
|
+
readonly kind: "target-locked";
|
|
83
86
|
} | {
|
|
84
87
|
readonly kind: "failed";
|
|
85
88
|
readonly failure: WriteFailure;
|
|
@@ -41,6 +41,8 @@ export function resolveAttachedField(registry, entityType, fieldName) {
|
|
|
41
41
|
return { kind: "unresolvable" };
|
|
42
42
|
return { kind: "resolved", fieldDef };
|
|
43
43
|
}
|
|
44
|
+
class AttachTargetLockedError extends Error {
|
|
45
|
+
}
|
|
44
46
|
function resolveUploadField(registry, input) {
|
|
45
47
|
const { entityType, entityId, fieldName, actionForm } = input;
|
|
46
48
|
if (actionForm === undefined)
|
|
@@ -196,14 +198,21 @@ export async function storeFileAsFileRef(deps, input) {
|
|
|
196
198
|
// skip: the create failure is the error worth reporting; the leftover bytes stay in storage.
|
|
197
199
|
}
|
|
198
200
|
};
|
|
201
|
+
const createRow = async (rowDb) => {
|
|
202
|
+
if (await deps.isAttachTargetLocked?.(rowDb))
|
|
203
|
+
throw new AttachTargetLockedError();
|
|
204
|
+
return createFileRefRow({ user: deps.user, tenantDb: rowDb }, upload.uploaded);
|
|
205
|
+
};
|
|
199
206
|
let row;
|
|
200
207
|
try {
|
|
201
208
|
row = deps.rowInOwnTransaction
|
|
202
|
-
? await runInOwnTransaction(deps.tenantDb,
|
|
203
|
-
: await
|
|
209
|
+
? await runInOwnTransaction(deps.tenantDb, createRow)
|
|
210
|
+
: await createRow(deps.tenantDb);
|
|
204
211
|
}
|
|
205
212
|
catch (e) {
|
|
206
213
|
await deleteUploadedBytes();
|
|
214
|
+
if (e instanceof AttachTargetLockedError)
|
|
215
|
+
return { kind: "target-locked" };
|
|
207
216
|
throw e;
|
|
208
217
|
}
|
|
209
218
|
if ("failure" in row) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RateLimitResolver } from "@cosmicdrift/kumiko-types/rate-limit-types";
|
|
1
2
|
import type { WriteErrorInfo } from "@cosmicdrift/kumiko-types/write-error-info-types";
|
|
2
3
|
import { Hono } from "hono";
|
|
3
4
|
import type { DbConnection } from "../db/connection.ts";
|
|
@@ -30,6 +31,7 @@ export type FileAccessGuard = (args: {
|
|
|
30
31
|
}) => FileAccessDecision | Promise<FileAccessDecision>;
|
|
31
32
|
export type FileRoutesOptions = {
|
|
32
33
|
readonly db: DbConnection;
|
|
34
|
+
readonly metaBatchRateLimitResolver?: RateLimitResolver;
|
|
33
35
|
readonly resolveProvider: FileProviderResolver;
|
|
34
36
|
readonly registry?: Registry;
|
|
35
37
|
readonly maxUploadSize?: string;
|
|
@@ -9,13 +9,14 @@ import { createProjectingTenantDb } from "../db/tenant-db.js";
|
|
|
9
9
|
import { createDerivativesContext, resolveFieldVariant, resolveRenderer, } from "../derivatives/index.js";
|
|
10
10
|
import { isAnonymousSessionUser, SYSTEM_USER_ID } from "../engine/system-user.js";
|
|
11
11
|
import { isFileField, isUuid, } from "../engine/types/index.js";
|
|
12
|
+
import { createFailOpenReporter, respondRateLimited } from "../rate-limit/middleware.js";
|
|
12
13
|
import { listActionFormFileFields, mayUseActionForm } from "./action-form-upload.js";
|
|
13
14
|
import { buildContentDispositionHeader } from "./content-disposition.js";
|
|
14
15
|
import { DEFAULT_MAX_UPLOAD_SIZE, getFileRefExecutor, storeFileAsFileRef, } from "./create-file-ref.js";
|
|
15
16
|
import { createFileContext, createTenantFileContext } from "./file-handle.js";
|
|
16
17
|
import { fileRefEntity } from "./file-ref-entity.js";
|
|
17
18
|
import { fileRefsTable } from "./file-ref-table.js";
|
|
18
|
-
import { RecordWriteDecisions } from "./record-write-check.js";
|
|
19
|
+
import { isStoredRecordLocked, RecordWriteDecisions, } from "./record-write-check.js";
|
|
19
20
|
import { neutralFileNameForMimeType } from "./resolve-content-type.js";
|
|
20
21
|
import { parseMaxSize, resolveServedContentType } from "./types.js";
|
|
21
22
|
import { withUploadLedger } from "./upload-ledger.js";
|
|
@@ -31,6 +32,8 @@ const DEFAULT_PRIVILEGED_ROLES = ["Admin", "SystemAdmin"];
|
|
|
31
32
|
// credential (see "Signed-URL default expiry" in core-files.md).
|
|
32
33
|
const SIGNED_URL_DEFAULT_EXPIRY_SECONDS = 15 * 60;
|
|
33
34
|
const MAX_BATCH_META_IDS = 100;
|
|
35
|
+
const META_BATCH_IDS_PER_WINDOW = 1_000;
|
|
36
|
+
const META_BATCH_WINDOW_SECONDS = 60;
|
|
34
37
|
// Each detail() read decrypts through the KMS pool; a bounded chunk keeps one batch from hogging it.
|
|
35
38
|
const BATCH_META_CONCURRENCY = 10;
|
|
36
39
|
// Default guard: on attached files, allow the uploader or a privileged role,
|
|
@@ -101,7 +104,8 @@ export function readFilesRouteOptions(registry) {
|
|
|
101
104
|
return {};
|
|
102
105
|
}
|
|
103
106
|
export function createFileRoutes(options) {
|
|
104
|
-
const { db } = options;
|
|
107
|
+
const { db, metaBatchRateLimitResolver } = options;
|
|
108
|
+
const reportMetaBatchLimiterFailOpen = createFailOpenReporter("files-meta-batch");
|
|
105
109
|
// Plain resolvers (not built via withFileProviderResolver) get the ledger on the route's own root connection.
|
|
106
110
|
const resolveProvider = options.resolveProvider.uploadLedgerFor
|
|
107
111
|
? options.resolveProvider
|
|
@@ -205,6 +209,10 @@ export function createFileRoutes(options) {
|
|
|
205
209
|
}
|
|
206
210
|
const data = new Uint8Array(await file.arrayBuffer());
|
|
207
211
|
const files = createTenantFileContext(resolveProvider, user.tenantId);
|
|
212
|
+
const { registry } = options;
|
|
213
|
+
const isAttachTargetLocked = registry !== undefined && entityType !== undefined && entityId !== undefined
|
|
214
|
+
? (rowDb) => isStoredRecordLocked({ registry, db: rowDb, entityType, entityId })
|
|
215
|
+
: undefined;
|
|
208
216
|
const outcome = await storeFileAsFileRef({
|
|
209
217
|
user,
|
|
210
218
|
tenantDb: tenantDbFor(user.tenantId),
|
|
@@ -216,6 +224,7 @@ export function createFileRoutes(options) {
|
|
|
216
224
|
await files.ref(key).delete();
|
|
217
225
|
await files.forgetUploadBytes(key);
|
|
218
226
|
},
|
|
227
|
+
...(isAttachTargetLocked ? { isAttachTargetLocked } : {}),
|
|
219
228
|
}, {
|
|
220
229
|
fileName: file.name,
|
|
221
230
|
declaredMimeType: file.type,
|
|
@@ -227,6 +236,9 @@ export function createFileRoutes(options) {
|
|
|
227
236
|
});
|
|
228
237
|
if (outcome.kind === "rejected")
|
|
229
238
|
return c.json({ error: outcome.error }, 400);
|
|
239
|
+
if (outcome.kind === "target-locked") {
|
|
240
|
+
return c.json({ error: "record_locked: the record is locked" }, 409);
|
|
241
|
+
}
|
|
230
242
|
if (outcome.kind === "failed")
|
|
231
243
|
return c.json({ error: "upload_failed" }, 500);
|
|
232
244
|
return c.json(outcome.fileRef, 201);
|
|
@@ -240,6 +252,21 @@ export function createFileRoutes(options) {
|
|
|
240
252
|
const ids = [...new Set((c.req.query("ids") ?? "").split(",").filter((id) => isUuid(id)))];
|
|
241
253
|
if (ids.length > MAX_BATCH_META_IDS)
|
|
242
254
|
return c.json({ error: "too_many_ids" }, 400);
|
|
255
|
+
if (metaBatchRateLimitResolver && ids.length > 0) {
|
|
256
|
+
const bucket = `files-meta:${user.tenantId}:${user.id}`;
|
|
257
|
+
try {
|
|
258
|
+
const decision = await metaBatchRateLimitResolver.check(bucket, {
|
|
259
|
+
limit: META_BATCH_IDS_PER_WINDOW,
|
|
260
|
+
windowSeconds: META_BATCH_WINDOW_SECONDS,
|
|
261
|
+
cost: ids.length,
|
|
262
|
+
});
|
|
263
|
+
if (!decision.allowed)
|
|
264
|
+
return respondRateLimited(c, decision, bucket);
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
reportMetaBatchLimiterFailOpen(e);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
243
270
|
const files = [];
|
|
244
271
|
for (let start = 0; start < ids.length; start += BATCH_META_CONCURRENCY) {
|
|
245
272
|
const chunk = ids.slice(start, start + BATCH_META_CONCURRENCY);
|
|
@@ -56,5 +56,6 @@ export type FileProviderResolverDeps = {
|
|
|
56
56
|
readonly secrets?: SecretsContext;
|
|
57
57
|
readonly db?: DbConnection | TenantDb;
|
|
58
58
|
readonly cacheSync?: CacheSyncBus;
|
|
59
|
+
readonly now?: () => Temporal.Instant;
|
|
59
60
|
};
|
|
60
61
|
export declare function makeFileProviderResolver(deps: FileProviderResolverDeps): FileProviderResolver;
|
|
@@ -119,5 +119,5 @@ export function makeFileProviderResolver(deps) {
|
|
|
119
119
|
cache.set(tenantId, built);
|
|
120
120
|
return built;
|
|
121
121
|
};
|
|
122
|
-
return deps.db ? withUploadLedger(resolveProvider, deps.db) : resolveProvider;
|
|
122
|
+
return deps.db ? withUploadLedger(resolveProvider, deps.db, deps.now) : resolveProvider;
|
|
123
123
|
}
|
|
@@ -20,3 +20,9 @@ export declare function createRecordWriteCheck(args: {
|
|
|
20
20
|
readonly canReadRecord: RecordReadCheck;
|
|
21
21
|
readonly tenantDbFor: (tenantId: TenantId) => TenantDb;
|
|
22
22
|
}): RecordWriteCheck;
|
|
23
|
+
export declare function isStoredRecordLocked(args: {
|
|
24
|
+
readonly registry: Registry;
|
|
25
|
+
readonly db: TenantDb;
|
|
26
|
+
readonly entityType: string;
|
|
27
|
+
readonly entityId: string;
|
|
28
|
+
}): Promise<boolean>;
|
|
@@ -62,3 +62,16 @@ export function createRecordWriteCheck(args) {
|
|
|
62
62
|
return RecordWriteDecisions.writable;
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
+
// The stored row, read through the given db so a transaction sees the lock state it commits against.
|
|
66
|
+
export async function isStoredRecordLocked(args) {
|
|
67
|
+
const { registry, db, entityType, entityId } = args;
|
|
68
|
+
const entity = registry.getEntity(entityType);
|
|
69
|
+
const table = findImplicitEntityTable(registry, entityType);
|
|
70
|
+
if (!entity || !table || entity.lockedWhen === undefined)
|
|
71
|
+
return false;
|
|
72
|
+
const row = await db.fetchOne(table, {
|
|
73
|
+
id: entityId,
|
|
74
|
+
...(entity.softDelete === true && { isDeleted: false }),
|
|
75
|
+
});
|
|
76
|
+
return row !== undefined && isRecordLocked(entity.lockedWhen, row);
|
|
77
|
+
}
|
|
@@ -5,6 +5,6 @@ import type { TenantId } from "@cosmicdrift/kumiko-types/identifiers";
|
|
|
5
5
|
import type { DbConnection } from "../db/connection.ts";
|
|
6
6
|
import { type TenantDb } from "../db/tenant-db.ts";
|
|
7
7
|
export declare const fileUploadLedgerTable: WritableTable;
|
|
8
|
-
export declare function createFileUploadLedger(db: DbConnection | TenantDb, tenantId: TenantId): FileUploadLedger;
|
|
9
|
-
export declare function withUploadLedger(resolver: (tenantId: TenantId) => Promise<FileStorageProvider>, db: DbConnection | TenantDb): FileProviderResolver;
|
|
8
|
+
export declare function createFileUploadLedger(db: DbConnection | TenantDb, tenantId: TenantId, now?: () => Temporal.Instant): FileUploadLedger;
|
|
9
|
+
export declare function withUploadLedger(resolver: (tenantId: TenantId) => Promise<FileStorageProvider>, db: DbConnection | TenantDb, now?: () => Temporal.Instant): FileProviderResolver;
|
|
10
10
|
export declare function takeUploadLedgerEntry(txDb: TenantDb, storageKey: string): Promise<boolean>;
|
|
@@ -21,7 +21,7 @@ function isTenantDb(db) {
|
|
|
21
21
|
}
|
|
22
22
|
// The ledger writes on a connection of its own, never on a handler transaction: a rollback of the
|
|
23
23
|
// handler must not take the row with it, or the bytes it uploaded would be unfindable.
|
|
24
|
-
export function createFileUploadLedger(db, tenantId) {
|
|
24
|
+
export function createFileUploadLedger(db, tenantId, now = () => Temporal.Now.instant()) {
|
|
25
25
|
const runner = isTenantDb(db) ? tenantDbRunner(db) : db;
|
|
26
26
|
if (!runnerCanBeginTransaction(runner)) {
|
|
27
27
|
throw new InternalError({
|
|
@@ -34,7 +34,7 @@ export function createFileUploadLedger(db, tenantId) {
|
|
|
34
34
|
await ledgerDb.insertOne(fileUploadLedgerTable, {
|
|
35
35
|
storageKey,
|
|
36
36
|
tenantId,
|
|
37
|
-
createdAt:
|
|
37
|
+
createdAt: now(),
|
|
38
38
|
});
|
|
39
39
|
},
|
|
40
40
|
forget: async (storageKey) => {
|
|
@@ -42,9 +42,9 @@ export function createFileUploadLedger(db, tenantId) {
|
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
-
export function withUploadLedger(resolver, db) {
|
|
45
|
+
export function withUploadLedger(resolver, db, now) {
|
|
46
46
|
return Object.assign(resolver, {
|
|
47
|
-
uploadLedgerFor: (tenantId) => createFileUploadLedger(db, tenantId),
|
|
47
|
+
uploadLedgerFor: (tenantId) => createFileUploadLedger(db, tenantId, now),
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
// Runs inside the transaction that creates the fileRef row. False means no ledger row was left to
|