@beechcms/core 0.8.0 → 0.9.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/dist/auth/account-email.d.ts +3 -0
- package/dist/auth/account-email.d.ts.map +1 -0
- package/dist/auth/account-email.js +9 -0
- package/dist/auth/password-reset-token.repository.d.ts +13 -2
- package/dist/auth/password-reset-token.repository.d.ts.map +1 -1
- package/dist/auth/user.repository.d.ts +69 -4
- package/dist/auth/user.repository.d.ts.map +1 -1
- package/dist/automations-grammar-words.d.ts +2 -0
- package/dist/automations-grammar-words.d.ts.map +1 -0
- package/dist/automations-grammar-words.js +8 -0
- package/dist/automations.repository.interface.d.ts +19 -0
- package/dist/automations.repository.interface.d.ts.map +1 -0
- package/dist/automations.repository.interface.js +3 -0
- package/dist/automations.runner.interface.d.ts +10 -0
- package/dist/automations.runner.interface.d.ts.map +1 -0
- package/dist/automations.runner.interface.js +3 -0
- package/dist/automations.runner.stub.d.ts +5 -0
- package/dist/automations.runner.stub.d.ts.map +1 -0
- package/dist/automations.runner.stub.js +7 -0
- package/dist/automations.types.d.ts +75 -0
- package/dist/automations.types.d.ts.map +1 -0
- package/dist/automations.types.js +3 -0
- package/dist/clock.d.ts +25 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +11 -0
- package/dist/common/canonical-json.d.ts +31 -0
- package/dist/common/canonical-json.d.ts.map +1 -0
- package/dist/common/canonical-json.js +91 -0
- package/dist/content/content-scan.repository.d.ts +1 -1
- package/dist/content/content.repository.d.ts +154 -3
- package/dist/content/content.repository.d.ts.map +1 -1
- package/dist/content/content.repository.js +62 -0
- package/dist/content/deletion-ledger.d.ts +39 -0
- package/dist/content/deletion-ledger.d.ts.map +1 -0
- package/dist/content/deletion-ledger.js +3 -0
- package/dist/content/seed-media-purge.repository.d.ts +34 -0
- package/dist/content/seed-media-purge.repository.d.ts.map +1 -0
- package/dist/content/seed-media-purge.repository.js +3 -0
- package/dist/content/seed.repository.d.ts +14 -0
- package/dist/content/seed.repository.d.ts.map +1 -1
- package/dist/content-scan.repository.d.ts +9 -0
- package/dist/content-scan.repository.d.ts.map +1 -0
- package/dist/content-scan.repository.js +3 -0
- package/dist/content.repository.d.ts +247 -0
- package/dist/content.repository.d.ts.map +1 -0
- package/dist/content.repository.js +59 -0
- package/dist/dashboard-layout/content-view.d.ts +757 -0
- package/dist/dashboard-layout/content-view.d.ts.map +1 -0
- package/dist/dashboard-layout/content-view.js +285 -0
- package/dist/dashboard-layout/content-view.repository.d.ts +39 -0
- package/dist/dashboard-layout/content-view.repository.d.ts.map +1 -0
- package/dist/dashboard-layout/content-view.repository.js +3 -0
- package/dist/dashboard-layout/dashboard-layout.d.ts +5 -0
- package/dist/dashboard-layout/dashboard-layout.d.ts.map +1 -1
- package/dist/dashboard-layout/dashboard-layout.js +17 -9
- package/dist/dashboard-layout/kanban/kanban.d.ts +32 -18
- package/dist/dashboard-layout/kanban/kanban.d.ts.map +1 -1
- package/dist/dashboard-layout/kanban/kanban.js +45 -21
- package/dist/dashboard-layout/seed-layout.d.ts +22 -31
- package/dist/dashboard-layout/seed-layout.d.ts.map +1 -1
- package/dist/dashboard-layout/seed-layout.js +93 -18
- package/dist/dashboard-layout/seed-layout.repository.d.ts +1 -5
- package/dist/dashboard-layout/seed-layout.repository.d.ts.map +1 -1
- package/dist/dashboard-layout.d.ts +154 -0
- package/dist/dashboard-layout.d.ts.map +1 -0
- package/dist/dashboard-layout.js +186 -0
- package/dist/dashboard-layout.repository.d.ts +18 -0
- package/dist/dashboard-layout.repository.d.ts.map +1 -0
- package/dist/dashboard-layout.repository.js +3 -0
- package/dist/dashboard-permissions.d.ts +3 -0
- package/dist/dashboard-permissions.d.ts.map +1 -0
- package/dist/dashboard-permissions.js +11 -0
- package/dist/dashboard-scopes.d.ts +12 -0
- package/dist/dashboard-scopes.d.ts.map +1 -0
- package/dist/dashboard-scopes.js +30 -0
- package/dist/define-seed.d.ts +3 -0
- package/dist/define-seed.d.ts.map +1 -0
- package/dist/define-seed.js +5 -0
- package/dist/demo-data.repository.d.ts +5 -0
- package/dist/demo-data.repository.d.ts.map +1 -0
- package/dist/demo-data.repository.js +3 -0
- package/dist/email.provider.d.ts +32 -0
- package/dist/email.provider.d.ts.map +1 -0
- package/dist/email.provider.js +4 -0
- package/dist/engine/ddl/ddl.d.ts +247 -0
- package/dist/engine/ddl/ddl.d.ts.map +1 -0
- package/dist/engine/ddl/ddl.js +653 -0
- package/dist/engine/ddl/schema-mutator.d.ts +29 -0
- package/dist/engine/ddl/schema-mutator.d.ts.map +1 -0
- package/dist/engine/ddl/schema-mutator.js +3 -0
- package/dist/engine/ddl/seed-ddl-destructive.d.ts +40 -0
- package/dist/engine/ddl/seed-ddl-destructive.d.ts.map +1 -0
- package/dist/engine/ddl/seed-ddl-destructive.js +149 -0
- package/dist/engine/ddl/seed-ddl.d.ts +43 -0
- package/dist/engine/ddl/seed-ddl.d.ts.map +1 -0
- package/dist/engine/ddl/seed-ddl.js +115 -0
- package/dist/engine/ddl/seed-purge-guards.d.ts +7 -0
- package/dist/engine/ddl/seed-purge-guards.d.ts.map +1 -0
- package/dist/engine/ddl/seed-purge-guards.js +19 -0
- package/dist/engine/engine.d.ts +4 -3
- package/dist/engine/engine.d.ts.map +1 -1
- package/dist/engine/engine.js +4 -3
- package/dist/engine/introspection/introspection.d.ts +101 -0
- package/dist/engine/introspection/introspection.d.ts.map +1 -0
- package/dist/engine/introspection/introspection.js +127 -0
- package/dist/engine/introspection/relation-audit.d.ts +46 -0
- package/dist/engine/introspection/relation-audit.d.ts.map +1 -0
- package/dist/engine/introspection/relation-audit.js +103 -0
- package/dist/engine/introspection/schema-fingerprint.d.ts +74 -0
- package/dist/engine/introspection/schema-fingerprint.d.ts.map +1 -0
- package/dist/engine/introspection/schema-fingerprint.js +102 -0
- package/dist/engine/introspection.d.ts +101 -0
- package/dist/engine/introspection.d.ts.map +1 -0
- package/dist/engine/introspection.js +127 -0
- package/dist/engine/localization/localization.d.ts +127 -0
- package/dist/engine/localization/localization.d.ts.map +1 -0
- package/dist/engine/localization/localization.js +212 -0
- package/dist/engine/localization.d.ts +127 -0
- package/dist/engine/localization.d.ts.map +1 -0
- package/dist/engine/localization.js +212 -0
- package/dist/engine/privacy/policies.d.ts +58 -0
- package/dist/engine/privacy/policies.d.ts.map +1 -0
- package/dist/engine/privacy/policies.js +191 -0
- package/dist/engine/privacy/privacy.service.d.ts +78 -0
- package/dist/engine/privacy/privacy.service.d.ts.map +1 -0
- package/dist/engine/privacy/privacy.service.js +138 -0
- package/dist/engine/privacy.service.d.ts.map +1 -1
- package/dist/engine/query/active-clause.d.ts +19 -0
- package/dist/engine/query/active-clause.d.ts.map +1 -0
- package/dist/engine/query/active-clause.js +27 -0
- package/dist/engine/query/query.d.ts +36 -0
- package/dist/engine/query/query.d.ts.map +1 -0
- package/dist/engine/query/query.js +393 -0
- package/dist/engine/query/relations.d.ts +28 -0
- package/dist/engine/query/relations.d.ts.map +1 -0
- package/dist/engine/query/relations.js +25 -0
- package/dist/engine/query/serialize.d.ts +22 -0
- package/dist/engine/query/serialize.d.ts.map +1 -0
- package/dist/engine/query/serialize.js +175 -0
- package/dist/engine/schema-fingerprint.d.ts +73 -0
- package/dist/engine/schema-fingerprint.d.ts.map +1 -0
- package/dist/engine/schema-fingerprint.js +97 -0
- package/dist/engine/seed-registry.d.ts.map +1 -1
- package/dist/engine/seeds/candidate-ids.d.ts +14 -0
- package/dist/engine/seeds/candidate-ids.d.ts.map +1 -0
- package/dist/engine/seeds/candidate-ids.js +40 -0
- package/dist/engine/seeds/define-seed.d.ts +3 -0
- package/dist/engine/seeds/define-seed.d.ts.map +1 -0
- package/dist/engine/seeds/define-seed.js +5 -0
- package/dist/engine/seeds/seed-definition-diff.d.ts +38 -0
- package/dist/engine/seeds/seed-definition-diff.d.ts.map +1 -0
- package/dist/engine/seeds/seed-definition-diff.js +51 -0
- package/dist/engine/seeds/seed-registry.d.ts +88 -0
- package/dist/engine/seeds/seed-registry.d.ts.map +1 -0
- package/dist/engine/seeds/seed-registry.js +194 -0
- package/dist/engine/seeds/seed-types-generator.d.ts +25 -0
- package/dist/engine/seeds/seed-types-generator.d.ts.map +1 -0
- package/dist/engine/seeds/seed-types-generator.js +116 -0
- package/dist/engine/seeds/seed-validation.d.ts +35 -0
- package/dist/engine/seeds/seed-validation.d.ts.map +1 -0
- package/dist/engine/seeds/seed-validation.js +384 -0
- package/dist/engine/seeds/seeds.d.ts +20 -0
- package/dist/engine/seeds/seeds.d.ts.map +1 -0
- package/dist/engine/seeds/seeds.js +21 -0
- package/dist/engine/seeds/tag-migration.d.ts +73 -0
- package/dist/engine/seeds/tag-migration.d.ts.map +1 -0
- package/dist/engine/seeds/tag-migration.js +212 -0
- package/dist/engine/seeds/tag-options.d.ts +15 -0
- package/dist/engine/seeds/tag-options.d.ts.map +1 -0
- package/dist/engine/seeds/tag-options.js +111 -0
- package/dist/engine/seeds/tag-value.d.ts +31 -0
- package/dist/engine/seeds/tag-value.d.ts.map +1 -0
- package/dist/engine/seeds/tag-value.js +51 -0
- package/dist/engine/types.d.ts +59 -7
- package/dist/engine/types.d.ts.map +1 -1
- package/dist/engine/validation/cache.d.ts.map +1 -1
- package/dist/engine/validation/cache.js +20 -4
- package/dist/engine/validation/emptiness.d.ts +12 -0
- package/dist/engine/validation/emptiness.d.ts.map +1 -0
- package/dist/engine/validation/emptiness.js +33 -0
- package/dist/engine/validation/index.d.ts +11 -0
- package/dist/engine/validation/index.d.ts.map +1 -1
- package/dist/engine/validation/index.js +29 -33
- package/dist/engine/validation/primitives.d.ts +12 -0
- package/dist/engine/validation/primitives.d.ts.map +1 -1
- package/dist/engine/validation/primitives.js +22 -0
- package/dist/engine/validation/richtext-sanitizer.d.ts +9 -1
- package/dist/engine/validation/richtext-sanitizer.d.ts.map +1 -1
- package/dist/engine/validation/richtext-sanitizer.js +36 -7
- package/dist/engine/validation/schema-builders.d.ts +16 -0
- package/dist/engine/validation/schema-builders.d.ts.map +1 -1
- package/dist/engine/validation/schema-builders.js +125 -18
- package/dist/engine.d.ts +144 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +741 -0
- package/dist/file-types.d.ts +13 -0
- package/dist/file-types.d.ts.map +1 -0
- package/dist/file-types.js +85 -0
- package/dist/hooks.d.ts +29 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +3 -0
- package/dist/id-generator.d.ts +26 -0
- package/dist/id-generator.d.ts.map +1 -0
- package/dist/id-generator.js +12 -0
- package/dist/idempotency.repository.d.ts +13 -0
- package/dist/idempotency.repository.d.ts.map +1 -0
- package/dist/idempotency.repository.js +3 -0
- package/dist/index.d.ts +34 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -11
- package/dist/kanban-position.repository.d.ts +19 -0
- package/dist/kanban-position.repository.d.ts.map +1 -0
- package/dist/kanban-position.repository.js +3 -0
- package/dist/kanban.d.ts +75 -0
- package/dist/kanban.d.ts.map +1 -0
- package/dist/kanban.js +82 -0
- package/dist/layout-permissions.d.ts +3 -0
- package/dist/layout-permissions.d.ts.map +1 -0
- package/dist/layout-permissions.js +11 -0
- package/dist/media/file-types.d.ts +12 -0
- package/dist/media/file-types.d.ts.map +1 -1
- package/dist/media/file-types.js +24 -4
- package/dist/media/image-transformer.d.ts +34 -0
- package/dist/media/image-transformer.d.ts.map +1 -0
- package/dist/media/image-transformer.js +3 -0
- package/dist/media/magic-bytes.d.ts.map +1 -1
- package/dist/media/magic-bytes.js +19 -9
- package/dist/media/media-transform.d.ts +92 -0
- package/dist/media/media-transform.d.ts.map +1 -0
- package/dist/media/media-transform.js +197 -0
- package/dist/media/media.repository.d.ts +5 -0
- package/dist/media/media.repository.d.ts.map +1 -1
- package/dist/media.repository.d.ts +61 -0
- package/dist/media.repository.d.ts.map +1 -0
- package/dist/media.repository.js +3 -0
- package/dist/notifications/notification.repository.d.ts +11 -2
- package/dist/notifications/notification.repository.d.ts.map +1 -1
- package/dist/oauth/authorization-code.repository.d.ts +10 -5
- package/dist/oauth/authorization-code.repository.d.ts.map +1 -1
- package/dist/oauth/role-guard.d.ts +30 -11
- package/dist/oauth/role-guard.d.ts.map +1 -1
- package/dist/oauth/role-guard.js +27 -5
- package/dist/oauth/token.repository.d.ts +2 -0
- package/dist/oauth/token.repository.d.ts.map +1 -1
- package/dist/policies.d.ts +10 -0
- package/dist/policies.d.ts.map +1 -0
- package/dist/policies.js +33 -0
- package/dist/queue/queue.interface.d.ts +9 -1
- package/dist/queue/queue.interface.d.ts.map +1 -1
- package/dist/queue/queue.stub.d.ts +1 -1
- package/dist/queue/queue.stub.d.ts.map +1 -1
- package/dist/queue/queue.stub.js +2 -3
- package/dist/queue.interface.d.ts +39 -0
- package/dist/queue.interface.d.ts.map +1 -0
- package/dist/queue.interface.js +3 -0
- package/dist/queue.stub.d.ts +6 -0
- package/dist/queue.stub.d.ts.map +1 -0
- package/dist/queue.stub.js +8 -0
- package/dist/rate-limit/token-bucket-rate-limiter.d.ts +7 -0
- package/dist/rate-limit/token-bucket-rate-limiter.d.ts.map +1 -1
- package/dist/rate-limit/token-bucket-rate-limiter.js +66 -0
- package/dist/rbac/evaluate.d.ts +57 -0
- package/dist/rbac/evaluate.d.ts.map +1 -0
- package/dist/rbac/evaluate.js +113 -0
- package/dist/rbac/invitation.repository.d.ts +84 -0
- package/dist/rbac/invitation.repository.d.ts.map +1 -0
- package/dist/rbac/invitation.repository.js +3 -0
- package/dist/rbac/permissions.d.ts +39 -0
- package/dist/rbac/permissions.d.ts.map +1 -0
- package/dist/rbac/permissions.js +43 -0
- package/dist/rbac/types.d.ts +130 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +4 -0
- package/dist/relations.d.ts +28 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/relations.js +25 -0
- package/dist/richtext-render.d.ts +11 -0
- package/dist/richtext-render.d.ts.map +1 -0
- package/dist/richtext-render.js +87 -0
- package/dist/richtext.d.ts +11 -0
- package/dist/richtext.d.ts.map +1 -0
- package/dist/richtext.js +13 -0
- package/dist/scheduler.interface.d.ts +4 -0
- package/dist/scheduler.interface.d.ts.map +1 -0
- package/dist/scheduler.interface.js +3 -0
- package/dist/scheduler.stub.d.ts +5 -0
- package/dist/scheduler.stub.d.ts.map +1 -0
- package/dist/scheduler.stub.js +7 -0
- package/dist/schema/canonical.d.ts +15 -0
- package/dist/schema/canonical.d.ts.map +1 -0
- package/dist/schema/canonical.js +37 -0
- package/dist/schema/define.d.ts +42 -0
- package/dist/schema/define.d.ts.map +1 -0
- package/dist/schema/define.js +56 -0
- package/dist/schema/emit.d.ts +14 -0
- package/dist/schema/emit.d.ts.map +1 -0
- package/dist/schema/emit.js +43 -0
- package/dist/schema/index.d.ts +12 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +13 -0
- package/dist/schema/manifest-seeds.d.ts +12 -0
- package/dist/schema/manifest-seeds.d.ts.map +1 -0
- package/dist/schema/manifest-seeds.js +48 -0
- package/dist/schema/manifest-validation.d.ts +13 -0
- package/dist/schema/manifest-validation.d.ts.map +1 -0
- package/dist/schema/manifest-validation.js +41 -0
- package/dist/schema/manifest.types.d.ts +54 -0
- package/dist/schema/manifest.types.d.ts.map +1 -0
- package/dist/schema/manifest.types.js +7 -0
- package/dist/schema-mutator.d.ts +26 -0
- package/dist/schema-mutator.d.ts.map +1 -0
- package/dist/schema-mutator.js +3 -0
- package/dist/search/search.repository.d.ts +5 -4
- package/dist/search/search.repository.d.ts.map +1 -1
- package/dist/search/vector-extractor.d.ts.map +1 -1
- package/dist/search/vector-extractor.js +23 -2
- package/dist/search/vector.repository.d.ts +2 -1
- package/dist/search/vector.repository.d.ts.map +1 -1
- package/dist/security/time-trap-token.repository.d.ts +5 -3
- package/dist/security/time-trap-token.repository.d.ts.map +1 -1
- package/dist/seed-ddl-destructive.d.ts +40 -0
- package/dist/seed-ddl-destructive.d.ts.map +1 -0
- package/dist/seed-ddl-destructive.js +149 -0
- package/dist/seed-ddl.d.ts +40 -0
- package/dist/seed-ddl.d.ts.map +1 -0
- package/dist/seed-ddl.js +100 -0
- package/dist/seed-layout.d.ts +145 -0
- package/dist/seed-layout.d.ts.map +1 -0
- package/dist/seed-layout.js +192 -0
- package/dist/seed-layout.repository.d.ts +22 -0
- package/dist/seed-layout.repository.d.ts.map +1 -0
- package/dist/seed-layout.repository.js +3 -0
- package/dist/seed-registry.d.ts +71 -0
- package/dist/seed-registry.d.ts.map +1 -0
- package/dist/seed-registry.js +143 -0
- package/dist/seed-types-generator.d.ts +1 -1
- package/dist/seed-types-generator.d.ts.map +1 -1
- package/dist/seed-validation.d.ts +23 -0
- package/dist/seed-validation.d.ts.map +1 -0
- package/dist/seed-validation.js +224 -0
- package/dist/seed.repository.d.ts +37 -0
- package/dist/seed.repository.d.ts.map +1 -0
- package/dist/seed.repository.js +3 -0
- package/dist/seeds.d.ts +20 -0
- package/dist/seeds.d.ts.map +1 -0
- package/dist/seeds.js +21 -0
- package/dist/settings/site-settings.repository.d.ts +8 -0
- package/dist/settings/site-settings.repository.d.ts.map +1 -1
- package/dist/site-settings.repository.d.ts +16 -0
- package/dist/site-settings.repository.d.ts.map +1 -0
- package/dist/site-settings.repository.js +3 -0
- package/dist/slug-utils.d.ts +18 -0
- package/dist/slug-utils.d.ts.map +1 -0
- package/dist/slug-utils.js +32 -0
- package/dist/storage.d.ts +46 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +3 -0
- package/dist/transfer/csv.d.ts +34 -0
- package/dist/transfer/csv.d.ts.map +1 -0
- package/dist/transfer/csv.js +135 -0
- package/dist/transfer/flat-seed.d.ts +13 -0
- package/dist/transfer/flat-seed.d.ts.map +1 -0
- package/dist/transfer/flat-seed.js +50 -0
- package/dist/transfer/index.d.ts +7 -0
- package/dist/transfer/index.d.ts.map +1 -0
- package/dist/transfer/index.js +8 -0
- package/dist/transfer/ndjson.d.ts +25 -0
- package/dist/transfer/ndjson.d.ts.map +1 -0
- package/dist/transfer/ndjson.js +62 -0
- package/dist/transfer/row-mapping.d.ts +36 -0
- package/dist/transfer/row-mapping.d.ts.map +1 -0
- package/dist/transfer/row-mapping.js +97 -0
- package/dist/transfer/transfer.constants.d.ts +20 -0
- package/dist/transfer/transfer.constants.d.ts.map +1 -0
- package/dist/transfer/transfer.constants.js +21 -0
- package/dist/transfer/transfer.types.d.ts +46 -0
- package/dist/transfer/transfer.types.d.ts.map +1 -0
- package/dist/transfer/transfer.types.js +6 -0
- package/dist/types.d.ts +243 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/validation.d.ts +50 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +660 -0
- package/dist/webhook-validation.d.ts +3 -0
- package/dist/webhook-validation.d.ts.map +1 -0
- package/dist/webhook-validation.js +6 -0
- package/package.json +11 -6
- package/dist/.tsbuildinfo +0 -1
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
3
|
+
import { AUTOMATION_RESERVED_WORDS } from '../../automations/automations-grammar-words.js';
|
|
4
|
+
import { SYSTEM_COLUMNS } from '../ddl/ddl.js';
|
|
5
|
+
import { LOCALIZABLE_BRANCH_TYPES } from '../localization/localization.js';
|
|
6
|
+
import { resolveClassification } from '../privacy/policies.js';
|
|
7
|
+
import { sortSeedsByDependencies } from './seed-registry.js';
|
|
8
|
+
import { tagOptionsIssues } from './tag-options.js';
|
|
9
|
+
import { SQL_RESERVED_WORDS } from '../sql-reserved-words.js';
|
|
10
|
+
const BRANCH_ID_RE = /^br_[A-Za-z0-9]+$/;
|
|
11
|
+
/**
|
|
12
|
+
* Allowed charset for a branch alias. An alias becomes a raw SQL column name in
|
|
13
|
+
* CREATE TABLE / ADD COLUMN / CREATE INDEX and in SELECT/WHERE/ORDER BY clauses
|
|
14
|
+
* (see engine.ts), so it MUST be restricted to a safe identifier charset to
|
|
15
|
+
* prevent DDL/query injection. Exported so the seeds API rename route reuses the
|
|
16
|
+
* exact same guard — do not inline a divergent copy.
|
|
17
|
+
*/
|
|
18
|
+
export const BRANCH_ALIAS_RE = /^[a-z][a-zA-Z0-9_]*$/;
|
|
19
|
+
/**
|
|
20
|
+
* Reason a branch alias is reserved, or `null` when free to use.
|
|
21
|
+
* Single source for seed validation and the branch rename route.
|
|
22
|
+
*/
|
|
23
|
+
export function reservedBranchAliasReason(alias) {
|
|
24
|
+
if (SYSTEM_COLUMNS.has(alias)) {
|
|
25
|
+
return `branch '${alias}' collides with reserved system column. ` +
|
|
26
|
+
`System columns (id, slug, status, created_at, updated_at) cannot be used as branch aliases.`;
|
|
27
|
+
}
|
|
28
|
+
if (AUTOMATION_RESERVED_WORDS.has(alias)) {
|
|
29
|
+
return `branch '${alias}' uses reserved alias. ` +
|
|
30
|
+
`This word is used by the automation template grammar.`;
|
|
31
|
+
}
|
|
32
|
+
if (SQL_RESERVED_WORDS.has(alias.toLowerCase())) {
|
|
33
|
+
return `branch '${alias}' uses SQL reserved keyword. ` +
|
|
34
|
+
`Please pick a different alias to prevent database errors.`;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Allowed charset for a seed slug identifier.
|
|
40
|
+
* Must consist solely of lowercase ASCII letters, digits, and underscores.
|
|
41
|
+
* Exported so route handlers and validation functions reuse the exact same guard.
|
|
42
|
+
*/
|
|
43
|
+
export const SEED_SLUG_RE = /^[a-z0-9_]+$/;
|
|
44
|
+
export const SLUG_RE = SEED_SLUG_RE;
|
|
45
|
+
/**
|
|
46
|
+
* Pure, console-free, throw-free validation of a seed set.
|
|
47
|
+
* The single seed being created/edited should be validated in the context of
|
|
48
|
+
* the full active set: call validateSeedDefinitions([...otherActiveSeeds, edited]).
|
|
49
|
+
*/
|
|
50
|
+
export function validateSeedDefinitions(seeds) {
|
|
51
|
+
const result = [];
|
|
52
|
+
// ── Fatal 1: missing or unknown relation targets ─────────────────────────
|
|
53
|
+
const slugSet = new Set(seeds.map(s => s.slug));
|
|
54
|
+
for (const seed of seeds) {
|
|
55
|
+
const messages = [];
|
|
56
|
+
for (const branch of seed.branches) {
|
|
57
|
+
if (branch.type !== 'relation')
|
|
58
|
+
continue;
|
|
59
|
+
if (!branch.targetSeed) {
|
|
60
|
+
messages.push(`branch '${branch.alias}' is a relation and requires a targetSeed`);
|
|
61
|
+
}
|
|
62
|
+
else if (!slugSet.has(branch.targetSeed)) {
|
|
63
|
+
messages.push(`branch '${branch.alias}' targets unknown seed '${branch.targetSeed}'`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (messages.length > 0)
|
|
67
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
68
|
+
}
|
|
69
|
+
// ── Fatal 2: multi-relation with SET NULL ────────────────────────────────
|
|
70
|
+
for (const seed of seeds) {
|
|
71
|
+
const messages = [];
|
|
72
|
+
for (const branch of seed.branches) {
|
|
73
|
+
if (branch.type === 'relation' && branch.multiple === true && branch.onDelete === 'SET NULL') {
|
|
74
|
+
messages.push(`Branch '${branch.alias}': multi-relations cannot use ON DELETE SET NULL. ` +
|
|
75
|
+
`Use 'CASCADE' or 'RESTRICT'.`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (messages.length > 0)
|
|
79
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
80
|
+
}
|
|
81
|
+
// ── Fatal 3: junction table name collisions / length ─────────────────────
|
|
82
|
+
{
|
|
83
|
+
const junctionNames = new Set();
|
|
84
|
+
for (const seed of seeds) {
|
|
85
|
+
const messages = [];
|
|
86
|
+
for (const branch of seed.branches) {
|
|
87
|
+
if (branch.type !== 'relation' || branch.multiple !== true)
|
|
88
|
+
continue;
|
|
89
|
+
const name = `rel_${seed.slug}_${branch.alias}`;
|
|
90
|
+
if (name.length > 256) {
|
|
91
|
+
messages.push(`Junction table name '${name}' exceeds 256 characters (${name.length})`);
|
|
92
|
+
}
|
|
93
|
+
if (junctionNames.has(name)) {
|
|
94
|
+
messages.push(`Junction table name collision: '${name}' already used by another seed/branch`);
|
|
95
|
+
}
|
|
96
|
+
junctionNames.add(name);
|
|
97
|
+
}
|
|
98
|
+
if (messages.length > 0)
|
|
99
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// ── Fatal 4: dependency cycles ────────────────────────────────────────────
|
|
103
|
+
const hasUnknownTargets = result.some(e => e.fatal);
|
|
104
|
+
if (!hasUnknownTargets) {
|
|
105
|
+
try {
|
|
106
|
+
sortSeedsByDependencies(seeds);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
110
|
+
const cycleMatch = msg.match(/Cyclic dependency detected among seeds: (.*)/);
|
|
111
|
+
if (cycleMatch) {
|
|
112
|
+
const cycleSlugs = cycleMatch[1].split(', ');
|
|
113
|
+
for (const slug of cycleSlugs) {
|
|
114
|
+
result.push({ slug, messages: [msg], fatal: true });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
result.push({ slug: '<graph>', messages: [msg], fatal: true });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// ── Fatal 5: branch id format / uniqueness ───────────────────────────────
|
|
123
|
+
for (const seed of seeds) {
|
|
124
|
+
const messages = [];
|
|
125
|
+
const idsInSeed = new Set();
|
|
126
|
+
for (const branch of seed.branches) {
|
|
127
|
+
if (!branch.id || !BRANCH_ID_RE.test(branch.id)) {
|
|
128
|
+
messages.push(`branch '${branch.alias}' has invalid id '${branch.id}'. Expected format ^br_[A-Za-z0-9]+$.`);
|
|
129
|
+
}
|
|
130
|
+
else if (idsInSeed.has(branch.id)) {
|
|
131
|
+
messages.push(`duplicate branch id '${branch.id}'`);
|
|
132
|
+
}
|
|
133
|
+
if (branch.id)
|
|
134
|
+
idsInSeed.add(branch.id);
|
|
135
|
+
// Alias becomes a raw SQL column name — reject anything outside the safe
|
|
136
|
+
// identifier charset before it can reach DDL/query string interpolation.
|
|
137
|
+
if (typeof branch.alias !== 'string' || !BRANCH_ALIAS_RE.test(branch.alias)) {
|
|
138
|
+
messages.push(`branch alias '${branch.alias}' is invalid. Expected format ${BRANCH_ALIAS_RE.source} ` +
|
|
139
|
+
`(lowercase letter followed by alphanumeric characters or underscores).`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (messages.length > 0)
|
|
143
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
144
|
+
}
|
|
145
|
+
// ── Fatal 6: reserved aliases ─────────────────────────────────────────────
|
|
146
|
+
for (const seed of seeds) {
|
|
147
|
+
const messages = [];
|
|
148
|
+
for (const branch of seed.branches) {
|
|
149
|
+
const reason = reservedBranchAliasReason(branch.alias);
|
|
150
|
+
if (reason)
|
|
151
|
+
messages.push(reason);
|
|
152
|
+
}
|
|
153
|
+
if (messages.length > 0)
|
|
154
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
155
|
+
}
|
|
156
|
+
// ── Fatal 10: repeater sub-field constraints ─────────────────────────────
|
|
157
|
+
{
|
|
158
|
+
const REPEATER_DISALLOWED_SUBTYPES = new Set(['repeater', 'relation', 'file']);
|
|
159
|
+
for (const seed of seeds) {
|
|
160
|
+
const messages = [];
|
|
161
|
+
for (const branch of seed.branches) {
|
|
162
|
+
if (branch.type !== 'repeater')
|
|
163
|
+
continue;
|
|
164
|
+
const subIds = new Set();
|
|
165
|
+
const subAliases = new Set();
|
|
166
|
+
for (const sub of branch.fields ?? []) {
|
|
167
|
+
if (REPEATER_DISALLOWED_SUBTYPES.has(sub.type)) {
|
|
168
|
+
messages.push(`branch '${branch.alias}': sub-field '${sub.alias}' has disallowed type '${sub.type}'. ` +
|
|
169
|
+
`Repeater sub-fields cannot be 'repeater', 'relation', or 'file' in v1.`);
|
|
170
|
+
}
|
|
171
|
+
if (!sub.id || !BRANCH_ID_RE.test(sub.id)) {
|
|
172
|
+
messages.push(`branch '${branch.alias}': sub-field '${sub.alias}' has invalid id '${sub.id}'. ` +
|
|
173
|
+
`Expected format ^br_[A-Za-z0-9]+$.`);
|
|
174
|
+
}
|
|
175
|
+
else if (subIds.has(sub.id)) {
|
|
176
|
+
messages.push(`branch '${branch.alias}': duplicate sub-field id '${sub.id}'`);
|
|
177
|
+
}
|
|
178
|
+
if (sub.id)
|
|
179
|
+
subIds.add(sub.id);
|
|
180
|
+
if (typeof sub.alias !== 'string' || !BRANCH_ALIAS_RE.test(sub.alias)) {
|
|
181
|
+
messages.push(`branch '${branch.alias}': sub-field alias '${sub.alias}' is invalid. ` +
|
|
182
|
+
`Expected format ${BRANCH_ALIAS_RE.source}.`);
|
|
183
|
+
}
|
|
184
|
+
else if (subAliases.has(sub.alias)) {
|
|
185
|
+
messages.push(`branch '${branch.alias}': duplicate sub-field alias '${sub.alias}'`);
|
|
186
|
+
}
|
|
187
|
+
subAliases.add(sub.alias);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (messages.length > 0)
|
|
191
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// ── Warning 7: duplicate slug ─────────────────────────────────────────────
|
|
195
|
+
{
|
|
196
|
+
const slugsSeen = new Set();
|
|
197
|
+
for (const seed of seeds) {
|
|
198
|
+
const messages = [];
|
|
199
|
+
if (slugsSeen.has(seed.slug)) {
|
|
200
|
+
messages.push(`duplicate slug "${seed.slug}" — each seed must have a unique slug`);
|
|
201
|
+
}
|
|
202
|
+
slugsSeen.add(seed.slug);
|
|
203
|
+
if (messages.length > 0)
|
|
204
|
+
result.push({ slug: seed.slug, messages, fatal: false });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// ── Warning 8: duplicate branch alias within a seed ───────────────────────
|
|
208
|
+
for (const seed of seeds) {
|
|
209
|
+
const messages = [];
|
|
210
|
+
const aliasesSeen = new Set();
|
|
211
|
+
for (const branch of seed.branches) {
|
|
212
|
+
if (aliasesSeen.has(branch.alias)) {
|
|
213
|
+
messages.push(`duplicate branch alias "${branch.alias}"`);
|
|
214
|
+
}
|
|
215
|
+
aliasesSeen.add(branch.alias);
|
|
216
|
+
}
|
|
217
|
+
if (messages.length > 0)
|
|
218
|
+
result.push({ slug: seed.slug, messages, fatal: false });
|
|
219
|
+
}
|
|
220
|
+
// ── Fatal 9: displayNameAlias not in branches ───────────────────────────
|
|
221
|
+
for (const seed of seeds) {
|
|
222
|
+
const allAliases = new Set(seed.branches.map(b => b.alias));
|
|
223
|
+
if (!allAliases.has(seed.displayNameAlias)) {
|
|
224
|
+
result.push({
|
|
225
|
+
slug: seed.slug,
|
|
226
|
+
messages: [`displayNameAlias "${seed.displayNameAlias}" not found in branches`],
|
|
227
|
+
fatal: true,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// ── Fatal 11 / Warning 10: repeater cardinality bounds ───────────────────
|
|
232
|
+
{
|
|
233
|
+
for (const seed of seeds) {
|
|
234
|
+
const fatals = [];
|
|
235
|
+
const warnings = [];
|
|
236
|
+
for (const branch of seed.branches) {
|
|
237
|
+
const hasBounds = branch.minItems !== undefined || branch.maxItems !== undefined;
|
|
238
|
+
if (!hasBounds)
|
|
239
|
+
continue;
|
|
240
|
+
if (branch.type !== 'repeater') {
|
|
241
|
+
warnings.push(`branch '${branch.alias}': minItems/maxItems are ignored on type ` +
|
|
242
|
+
`'${branch.type}' (repeater-only).`);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
for (const [key, val] of [['minItems', branch.minItems], ['maxItems', branch.maxItems]]) {
|
|
246
|
+
if (val !== undefined && (!Number.isInteger(val) || val < 0)) {
|
|
247
|
+
fatals.push(`branch '${branch.alias}': ${key} must be a non-negative integer (got ${val}).`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (Number.isInteger(branch.minItems) && Number.isInteger(branch.maxItems) &&
|
|
251
|
+
branch.minItems > branch.maxItems) {
|
|
252
|
+
fatals.push(`branch '${branch.alias}': minItems (${branch.minItems}) must be <= maxItems (${branch.maxItems}).`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (fatals.length > 0)
|
|
256
|
+
result.push({ slug: seed.slug, messages: fatals, fatal: true });
|
|
257
|
+
if (warnings.length > 0)
|
|
258
|
+
result.push({ slug: seed.slug, messages: warnings, fatal: false });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ── Fatal 12: displayNameAlias format validation ───────────────────────────
|
|
262
|
+
for (const seed of seeds) {
|
|
263
|
+
if (typeof seed.displayNameAlias !== 'string' || !BRANCH_ALIAS_RE.test(seed.displayNameAlias)) {
|
|
264
|
+
result.push({
|
|
265
|
+
slug: seed.slug,
|
|
266
|
+
messages: [
|
|
267
|
+
`displayNameAlias '${seed.displayNameAlias}' is invalid. Expected format ${BRANCH_ALIAS_RE.source} ` +
|
|
268
|
+
`(lowercase letter followed by alphanumeric characters or underscores).`,
|
|
269
|
+
],
|
|
270
|
+
fatal: true,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
else if (SYSTEM_COLUMNS.has(seed.displayNameAlias)) {
|
|
274
|
+
result.push({
|
|
275
|
+
slug: seed.slug,
|
|
276
|
+
messages: [
|
|
277
|
+
`displayNameAlias '${seed.displayNameAlias}' collides with reserved system column. ` +
|
|
278
|
+
`System columns (id, slug, status, created_at, updated_at) cannot be used as displayNameAlias.`,
|
|
279
|
+
],
|
|
280
|
+
fatal: true,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
else if (SQL_RESERVED_WORDS.has(seed.displayNameAlias.toLowerCase())) {
|
|
284
|
+
result.push({
|
|
285
|
+
slug: seed.slug,
|
|
286
|
+
messages: [
|
|
287
|
+
`displayNameAlias '${seed.displayNameAlias}' uses SQL reserved keyword. ` +
|
|
288
|
+
`Please pick a different alias to prevent database errors.`,
|
|
289
|
+
],
|
|
290
|
+
fatal: true,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// ── Fatal 13: retentionDays validation ─────────────────────────────────────
|
|
295
|
+
for (const seed of seeds) {
|
|
296
|
+
if (seed.retentionDays !== undefined && (!Number.isInteger(seed.retentionDays) || seed.retentionDays <= 0)) {
|
|
297
|
+
result.push({
|
|
298
|
+
slug: seed.slug,
|
|
299
|
+
messages: [
|
|
300
|
+
`retentionDays must be a positive integer >= 1 (got ${seed.retentionDays}).`,
|
|
301
|
+
],
|
|
302
|
+
fatal: true,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// ── Fatal 14: publicEdit validation ─────────────────────────────────────────
|
|
307
|
+
for (const seed of seeds) {
|
|
308
|
+
const messages = [];
|
|
309
|
+
for (const branch of seed.branches) {
|
|
310
|
+
if (branch.policies?.publicEdit !== undefined && typeof branch.policies.publicEdit !== 'boolean') {
|
|
311
|
+
messages.push(`branch '${branch.alias}': policies.publicEdit must be a boolean`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (messages.length > 0)
|
|
315
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
316
|
+
}
|
|
317
|
+
// ── Fatal 15: seed slug format validation ─────────────────────────────────
|
|
318
|
+
for (const seed of seeds) {
|
|
319
|
+
if (typeof seed.slug !== 'string' || !SEED_SLUG_RE.test(seed.slug)) {
|
|
320
|
+
result.push({
|
|
321
|
+
slug: seed.slug,
|
|
322
|
+
messages: [
|
|
323
|
+
`slug '${seed.slug}' is invalid. Expected format ${SEED_SLUG_RE.source} ` +
|
|
324
|
+
`(lowercase letters, numbers, and underscores only).`,
|
|
325
|
+
],
|
|
326
|
+
fatal: true,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// ── Fatal 16: softDelete type validation ────────────────────────────────────
|
|
331
|
+
for (const seed of seeds) {
|
|
332
|
+
if (seed.softDelete !== undefined && typeof seed.softDelete !== 'boolean') {
|
|
333
|
+
result.push({
|
|
334
|
+
slug: seed.slug,
|
|
335
|
+
messages: [`softDelete must be a boolean (got ${typeof seed.softDelete}).`],
|
|
336
|
+
fatal: true,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// ── Fatal 17: field-level localization constraints ─────────────────────────
|
|
341
|
+
// A localized value is a readable locale dictionary. Encrypted or hashed storage turns it into an
|
|
342
|
+
// opaque blob: per-locale extraction would be meaningless and would break decryption. Repeater
|
|
343
|
+
// sub-fields are excluded by domain rule, whatever their type.
|
|
344
|
+
for (const seed of seeds) {
|
|
345
|
+
const messages = [];
|
|
346
|
+
for (const branch of seed.branches) {
|
|
347
|
+
if (branch.localized !== undefined && typeof branch.localized !== 'boolean') {
|
|
348
|
+
messages.push(`branch '${branch.alias}': localized must be a boolean`);
|
|
349
|
+
}
|
|
350
|
+
else if (branch.localized === true) {
|
|
351
|
+
if (!LOCALIZABLE_BRANCH_TYPES.has(branch.type)) {
|
|
352
|
+
messages.push(`branch '${branch.alias}': localized is only supported on text, richtext and json branches ` +
|
|
353
|
+
`(got '${branch.type}').`);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
const { classification, storage } = resolveClassification(branch);
|
|
357
|
+
if (storage !== 'plain') {
|
|
358
|
+
messages.push(`branch '${branch.alias}': localized cannot be combined with '${classification}' classification ` +
|
|
359
|
+
`(values are stored ${storage === 'encrypt' ? 'encrypted' : 'hashed'}).`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
for (const sub of branch.fields ?? []) {
|
|
364
|
+
if (sub.localized !== undefined && sub.localized !== false) {
|
|
365
|
+
messages.push(`branch '${branch.alias}': sub-field '${sub.alias}' cannot be localized. ` +
|
|
366
|
+
`Repeater sub-fields are never localized.`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (messages.length > 0)
|
|
371
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
372
|
+
}
|
|
373
|
+
// ── Fatal 18: tags options / cardinality ───────────────────────────────────
|
|
374
|
+
for (const seed of seeds) {
|
|
375
|
+
const messages = seed.branches.flatMap(branch => tagOptionsIssues(branch));
|
|
376
|
+
if (messages.length > 0)
|
|
377
|
+
result.push({ slug: seed.slug, messages, fatal: true });
|
|
378
|
+
}
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
/** Convenience: true iff no fatal issues. */
|
|
382
|
+
export function isSeedSetValid(seeds) {
|
|
383
|
+
return validateSeedDefinitions(seeds).every(i => !i.fatal);
|
|
384
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed Registry: configurazione degli schemi di contenuto.
|
|
3
|
+
* In v0.4.0 ogni Seed genera una tabella `content_{slug}` con colonne reali.
|
|
4
|
+
* `branch.alias` è il nome della colonna SQL.
|
|
5
|
+
*/
|
|
6
|
+
import type { Seed } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Registro globale dei Seed.
|
|
9
|
+
* In produzione, questo viene popolato dalle definizioni dell'utente.
|
|
10
|
+
*/
|
|
11
|
+
export declare const SEED_REGISTRY: Record<string, Seed>;
|
|
12
|
+
/**
|
|
13
|
+
* Registra un set di Seed nel registro globale.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerSeeds(seeds: Seed[]): void;
|
|
16
|
+
/**
|
|
17
|
+
* Ritorna il Seed per lo slug dato, o null se non esiste.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getSeed(slug: string): Seed | null;
|
|
20
|
+
//# sourceMappingURL=seeds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeds.d.ts","sourceRoot":"","sources":["../../../src/engine/seeds/seeds.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAM,CAAA;AAErD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,QAI1C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEjD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
3
|
+
/**
|
|
4
|
+
* Registro globale dei Seed.
|
|
5
|
+
* In produzione, questo viene popolato dalle definizioni dell'utente.
|
|
6
|
+
*/
|
|
7
|
+
export const SEED_REGISTRY = {};
|
|
8
|
+
/**
|
|
9
|
+
* Registra un set di Seed nel registro globale.
|
|
10
|
+
*/
|
|
11
|
+
export function registerSeeds(seeds) {
|
|
12
|
+
seeds.forEach(seed => {
|
|
13
|
+
SEED_REGISTRY[seed.slug] = seed;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Ritorna il Seed per lo slug dato, o null se non esiste.
|
|
18
|
+
*/
|
|
19
|
+
export function getSeed(slug) {
|
|
20
|
+
return SEED_REGISTRY[slug] ?? null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module engine/tag-migration
|
|
3
|
+
* Data migration from the pre-0.9 tag/choice shapes to the closed-vocabulary `tags` contract.
|
|
4
|
+
*
|
|
5
|
+
* Before 0.9 a choice field was either `text` + `options` (a single value stored as a plain string)
|
|
6
|
+
* or `tags` + `options: string[]`. Since 0.9 `options` exists only on `tags` as `{ value, color }[]`,
|
|
7
|
+
* and every stored value is a JSON array. Databases holding the old shapes were rejected at boot with
|
|
8
|
+
* no way forward; this module computes the way forward.
|
|
9
|
+
*
|
|
10
|
+
* Everything here is pure: it plans from seed definitions and emits SQL text. Nothing is executed,
|
|
11
|
+
* so the same plan drives the CLI dry run, `--apply`, and the audit.
|
|
12
|
+
*/
|
|
13
|
+
import type { Seed } from '../types.js';
|
|
14
|
+
import type { SchemaQueryExecutor } from '../introspection/introspection.js';
|
|
15
|
+
/**
|
|
16
|
+
* Colors assigned, in declaration order, to legacy options that never carried one.
|
|
17
|
+
* Deterministic on purpose: re-running the plan yields the same definition.
|
|
18
|
+
*/
|
|
19
|
+
export declare const TAG_MIGRATION_PALETTE: readonly string[];
|
|
20
|
+
/** What the plan does with one branch. */
|
|
21
|
+
export type TagMigrationAction =
|
|
22
|
+
/** `text` + `options` becomes single-choice `tags`; stored strings are wrapped into arrays. */
|
|
23
|
+
'text-to-tags'
|
|
24
|
+
/** `tags` whose `options` are plain strings or carry invalid colors; stored values are untouched. */
|
|
25
|
+
| 'tags-options'
|
|
26
|
+
/** Cannot be converted safely; a human must edit the seed. */
|
|
27
|
+
| 'manual';
|
|
28
|
+
export interface TagMigrationStep {
|
|
29
|
+
seed: string;
|
|
30
|
+
/** Dotted path when the branch is nested in a repeater, otherwise the alias. */
|
|
31
|
+
alias: string;
|
|
32
|
+
action: TagMigrationAction;
|
|
33
|
+
/** One line, operator-facing. */
|
|
34
|
+
summary: string;
|
|
35
|
+
/** Non-fatal remarks (e.g. a full-text index that still covers the column). */
|
|
36
|
+
warnings: string[];
|
|
37
|
+
}
|
|
38
|
+
export interface TagMigrationPlan {
|
|
39
|
+
steps: TagMigrationStep[];
|
|
40
|
+
/** Rewritten definitions, only for seeds that changed. Seeds with a `manual` step are not rewritten. */
|
|
41
|
+
seeds: Seed[];
|
|
42
|
+
}
|
|
43
|
+
/** True when the plan has anything to report or do. */
|
|
44
|
+
export declare function hasTagMigrationWork(plan: TagMigrationPlan): boolean;
|
|
45
|
+
/** Steps a human must resolve. */
|
|
46
|
+
export declare function manualTagSteps(plan: TagMigrationPlan): TagMigrationStep[];
|
|
47
|
+
/**
|
|
48
|
+
* Computes what moving `seeds` to the 0.9 tags contract takes. Seeds that already comply produce no
|
|
49
|
+
* step and no rewritten definition, so a clean database yields an empty plan (the operation is idempotent).
|
|
50
|
+
*/
|
|
51
|
+
export declare function planTagMigration(seeds: readonly Seed[]): TagMigrationPlan;
|
|
52
|
+
/**
|
|
53
|
+
* The statements that carry the plan out, in execution order: stored values first, then the seed
|
|
54
|
+
* definitions, then the registry-version bump that tells every isolate to rebuild its cached registry.
|
|
55
|
+
* Value wrapping touches the live table and, when drafts are on, the drafts table.
|
|
56
|
+
*
|
|
57
|
+
* `originals` are the definitions the plan was computed from (the physical tables are named after them).
|
|
58
|
+
*/
|
|
59
|
+
export declare function tagMigrationStatements(plan: TagMigrationPlan, originals: readonly Seed[]): string[];
|
|
60
|
+
/** A stored value that is not in the (new) vocabulary of its branch. */
|
|
61
|
+
export interface TagOrphan {
|
|
62
|
+
seed: string;
|
|
63
|
+
alias: string;
|
|
64
|
+
value: string;
|
|
65
|
+
rows: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reads, before anything is changed, the stored values the new vocabulary would not contain. They are
|
|
69
|
+
* reported, never deleted or rewritten: the operator decides whether to add an option or fix the rows.
|
|
70
|
+
* Read-only (`SELECT` through the injected executor).
|
|
71
|
+
*/
|
|
72
|
+
export declare function findTagOrphans(executor: SchemaQueryExecutor, plan: TagMigrationPlan, originals: readonly Seed[]): Promise<TagOrphan[]>;
|
|
73
|
+
//# sourceMappingURL=tag-migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag-migration.d.ts","sourceRoot":"","sources":["../../../src/engine/seeds/tag-migration.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAU,IAAI,EAAa,MAAM,aAAa,CAAA;AAE1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAA;AAK5E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAElD,CAAA;AAED,0CAA0C;AAC1C,MAAM,MAAM,kBAAkB;AAC5B,+FAA+F;AAC7F,cAAc;AAChB,qGAAqG;GACnG,cAAc;AAChB,8DAA8D;GAC5D,QAAQ,CAAA;AAEZ,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,kBAAkB,CAAA;IAC1B,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,gBAAgB,EAAE,CAAA;IACzB,wGAAwG;IACxG,KAAK,EAAE,IAAI,EAAE,CAAA;CACd;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAEnE;AAED,kCAAkC;AAClC,wBAAgB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAEzE;AA2GD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,gBAAgB,CAuBzE;AAeD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,IAAI,EAAE,GAAG,MAAM,EAAE,CA2BnG;AAED,wEAAwE;AACxE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb;AAOD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,mBAAmB,EAC7B,IAAI,EAAE,gBAAgB,EACtB,SAAS,EAAE,SAAS,IAAI,EAAE,GACzB,OAAO,CAAC,SAAS,EAAE,CAAC,CA2BtB"}
|