@beechcms/core 0.5.0 → 0.6.0-preview.2
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/hash-provider.d.ts.map +1 -1
- package/dist/auth/hash-provider.js +2 -0
- package/dist/auth/password-reset-token.repository.d.ts.map +1 -1
- package/dist/auth/password-reset-token.repository.js +2 -0
- package/dist/auth/session.repository.d.ts.map +1 -1
- package/dist/auth/session.repository.js +2 -0
- package/dist/auth/token-service.d.ts +2 -0
- package/dist/auth/token-service.d.ts.map +1 -1
- package/dist/auth/token-service.js +2 -0
- package/dist/auth/user.repository.d.ts +4 -1
- package/dist/auth/user.repository.d.ts.map +1 -1
- package/dist/auth/user.repository.js +2 -0
- package/dist/automations-grammar-words.d.ts.map +1 -1
- package/dist/automations-grammar-words.js +2 -0
- package/dist/automations.repository.interface.d.ts.map +1 -1
- package/dist/automations.repository.interface.js +2 -0
- package/dist/automations.runner.interface.d.ts.map +1 -1
- package/dist/automations.runner.interface.js +2 -0
- package/dist/automations.runner.stub.d.ts.map +1 -1
- package/dist/automations.runner.stub.js +2 -0
- package/dist/automations.types.d.ts +2 -0
- package/dist/automations.types.d.ts.map +1 -1
- package/dist/automations.types.js +2 -0
- package/dist/clock.d.ts.map +1 -1
- package/dist/clock.js +2 -0
- package/dist/content-scan.repository.d.ts.map +1 -1
- package/dist/content-scan.repository.js +2 -0
- package/dist/content.repository.d.ts +62 -0
- package/dist/content.repository.d.ts.map +1 -1
- package/dist/content.repository.js +18 -0
- 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.map +1 -1
- package/dist/define-seed.js +2 -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/engine.d.ts +76 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +259 -5
- 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/id-generator.d.ts +7 -0
- package/dist/id-generator.d.ts.map +1 -1
- package/dist/id-generator.js +5 -0
- package/dist/idempotency.repository.d.ts.map +1 -1
- package/dist/idempotency.repository.js +2 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- 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.repository.d.ts.map +1 -1
- package/dist/media.repository.js +2 -0
- package/dist/notifications/notification-service.d.ts.map +1 -1
- package/dist/notifications/notification-service.js +2 -0
- package/dist/notifications/notification.repository.d.ts.map +1 -1
- package/dist/notifications/notification.repository.js +2 -9
- package/dist/observability/activity-log.repository.d.ts.map +1 -1
- package/dist/observability/activity-log.repository.js +2 -0
- package/dist/observability/activity-logger.d.ts +2 -2
- package/dist/observability/activity-logger.d.ts.map +1 -1
- package/dist/observability/activity-logger.js +2 -11
- package/dist/observability/analytics.repository.d.ts.map +1 -1
- package/dist/observability/analytics.repository.js +2 -0
- package/dist/policies.d.ts.map +1 -1
- package/dist/policies.js +8 -3
- package/dist/rate-limit/rate-limiter.d.ts.map +1 -1
- package/dist/rate-limit/rate-limiter.js +2 -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.map +1 -1
- package/dist/richtext-render.js +2 -0
- package/dist/richtext.d.ts.map +1 -1
- package/dist/richtext.js +2 -0
- package/dist/scheduler.interface.d.ts.map +1 -1
- package/dist/scheduler.interface.js +2 -0
- package/dist/scheduler.stub.d.ts.map +1 -1
- package/dist/scheduler.stub.js +2 -0
- package/dist/schema-mutator.d.ts +22 -0
- package/dist/schema-mutator.d.ts.map +1 -0
- package/dist/schema-mutator.js +3 -0
- package/dist/search/search.repository.d.ts.map +1 -1
- package/dist/search/search.repository.js +2 -0
- 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 +117 -0
- package/dist/seed-layout.d.ts.map +1 -0
- package/dist/seed-layout.js +169 -0
- package/dist/seed-layout.repository.d.ts +18 -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 +23 -1
- package/dist/seed-registry.d.ts.map +1 -1
- package/dist/seed-registry.js +105 -0
- 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.map +1 -1
- package/dist/seeds.js +2 -0
- 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.map +1 -1
- package/dist/slug-utils.js +2 -0
- package/dist/storage.d.ts +12 -38
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +2 -0
- package/dist/types.d.ts +81 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/dist/validation.d.ts +23 -17
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +487 -381
- 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/dist/widget/widget.repository.d.ts +32 -3
- package/dist/widget/widget.repository.d.ts.map +1 -1
- package/dist/widget/widget.repository.js +6 -1
- package/package.json +5 -4
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module @beechcms/core/observability/activity-logger
|
|
11
11
|
*/
|
|
12
|
-
export type ActivityAction = 'create' | 'update' | 'delete' | 'upload';
|
|
13
|
-
export type EntityType = 'content' | 'media';
|
|
12
|
+
export type ActivityAction = 'create' | 'update' | 'delete' | 'upload' | 'bulk_update';
|
|
13
|
+
export type EntityType = 'content' | 'media' | 'seed';
|
|
14
14
|
/**
|
|
15
15
|
* Identifies the human (or machine) actor that triggered the event.
|
|
16
16
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-logger.d.ts","sourceRoot":"","sources":["../../src/observability/activity-logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"activity-logger.d.ts","sourceRoot":"","sources":["../../src/observability/activity-logger.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,aAAa,CAAA;AACtF,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAA;AAErD;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,KAAK,EAAE,aAAa,CAAA;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;OASG;IACH,GAAG,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CACnD"}
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Records side-effect events ("a user did X to entity Y") for the audit trail
|
|
5
|
-
* displayed in the dashboard's settings activity tab and the recent activity
|
|
6
|
-
* feed. The interface is intentionally framework-agnostic so it can be
|
|
7
|
-
* fulfilled in production by a D1-backed implementation, in tests by an
|
|
8
|
-
* in-memory implementation, and in the future by remote sinks.
|
|
9
|
-
*
|
|
10
|
-
* @module @beechcms/core/observability/activity-logger
|
|
11
|
-
*/
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
12
3
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.repository.d.ts","sourceRoot":"","sources":["../../src/observability/analytics.repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analytics.repository.d.ts","sourceRoot":"","sources":["../../src/observability/analytics.repository.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,UAAU,CAAA;AAErD;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE9C;;;;OAIG;IACH,WAAW,CACT,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CAAA;IAElB;;;;OAIG;IACH,aAAa,CACX,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACnC"}
|
package/dist/policies.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK9D;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEzF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAgBzF"}
|
package/dist/policies.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
1
3
|
export async function sha256hex(value) {
|
|
2
4
|
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));
|
|
3
5
|
return Array.from(new Uint8Array(buf))
|
|
@@ -17,12 +19,15 @@ export function resolvePolicies(branch) {
|
|
|
17
19
|
// Non-plain privacy implies hidden by default: the CMS hashes/encrypts on write,
|
|
18
20
|
// so returning the stored value would leak the digest to readers.
|
|
19
21
|
const defaultVisibility = privacy !== 'plain' ? 'hidden' : 'full';
|
|
22
|
+
// Repeaters live in a single JSON column — never filterable, sortable, or
|
|
23
|
+
// searchable/facetable in v1, regardless of what's set on the branch.
|
|
24
|
+
const isRepeater = branch.type === 'repeater';
|
|
20
25
|
return {
|
|
21
26
|
privacy,
|
|
22
27
|
visibility: branch.policies?.visibility ?? defaultVisibility,
|
|
23
|
-
search: branch.policies?.search ?? true,
|
|
24
|
-
filter: branch.policies?.filter ?? true,
|
|
25
|
-
sort: branch.policies?.sort ?? true,
|
|
28
|
+
search: isRepeater ? false : branch.policies?.search ?? true,
|
|
29
|
+
filter: isRepeater ? false : branch.policies?.filter ?? true,
|
|
30
|
+
sort: isRepeater ? false : branch.policies?.sort ?? true,
|
|
26
31
|
public: branch.policies?.public ?? true,
|
|
27
32
|
};
|
|
28
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/rate-limit/rate-limiter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/rate-limit/rate-limiter.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAA;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CAClD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Back-reference helpers for the Beech CMS relation system.
|
|
3
|
+
*
|
|
4
|
+
* `buildBackrefMap` is a pure function that iterates the seed registry once
|
|
5
|
+
* and builds a lookup map: targetSlug → sources that point to it.
|
|
6
|
+
* Build this once at app factory time and cache it for the process lifetime.
|
|
7
|
+
*/
|
|
8
|
+
import type { Seed } from './types.js';
|
|
9
|
+
export interface BackrefSource {
|
|
10
|
+
/** Slug of the seed that owns the relation branch */
|
|
11
|
+
sourceSlug: string;
|
|
12
|
+
/** `branch.alias` — the FK column name (single) or join-table name suffix (multi) */
|
|
13
|
+
branchAlias: string;
|
|
14
|
+
/** `branch.label` — human label for UI grouping */
|
|
15
|
+
branchLabel: string;
|
|
16
|
+
/** 'single' for direct FK, 'multi' for many-to-many join table */
|
|
17
|
+
relationship: 'single' | 'multi';
|
|
18
|
+
/** True when branch.onDelete === 'RESTRICT' — used to disable Delete button */
|
|
19
|
+
restricts: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Map: targetSlug → every source that points to it */
|
|
22
|
+
export type BackrefMap = Map<string, BackrefSource[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Builds the back-reference map from an array of seeds.
|
|
25
|
+
* Pure function — no side-effects, safe to call at startup and cache forever.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildBackrefMap(seeds: Seed[]): BackrefMap;
|
|
28
|
+
//# sourceMappingURL=relations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAA;IACnB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAA;IACnB,kEAAkE;IAClE,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAA;IAChC,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,uDAAuD;AACvD,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAA;AAErD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,UAAU,CAoBzD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
3
|
+
/**
|
|
4
|
+
* Builds the back-reference map from an array of seeds.
|
|
5
|
+
* Pure function — no side-effects, safe to call at startup and cache forever.
|
|
6
|
+
*/
|
|
7
|
+
export function buildBackrefMap(seeds) {
|
|
8
|
+
const map = new Map();
|
|
9
|
+
for (const seed of seeds) {
|
|
10
|
+
for (const branch of seed.branches) {
|
|
11
|
+
if (branch.type !== 'relation' || !branch.targetSeed)
|
|
12
|
+
continue;
|
|
13
|
+
const existing = map.get(branch.targetSeed) ?? [];
|
|
14
|
+
existing.push({
|
|
15
|
+
sourceSlug: seed.slug,
|
|
16
|
+
branchAlias: branch.alias,
|
|
17
|
+
branchLabel: branch.label,
|
|
18
|
+
relationship: branch.multiple === true ? 'multi' : 'single',
|
|
19
|
+
restricts: branch.onDelete === 'RESTRICT',
|
|
20
|
+
});
|
|
21
|
+
map.set(branch.targetSeed, existing);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return map;
|
|
25
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"richtext-render.d.ts","sourceRoot":"","sources":["../src/richtext-render.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"richtext-render.d.ts","sourceRoot":"","sources":["../src/richtext-render.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAuD/C;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,GAAG,IAAI,CAatF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQrD"}
|
package/dist/richtext-render.js
CHANGED
package/dist/richtext.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"richtext.d.ts","sourceRoot":"","sources":["../src/richtext.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"richtext.d.ts","sourceRoot":"","sources":["../src/richtext.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAG,CAAU,CAAA;AAEjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,OAAO,uBAAuB,CAAA;IAC7C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAIhF"}
|
package/dist/richtext.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.interface.d.ts","sourceRoot":"","sources":["../src/scheduler.interface.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scheduler.interface.d.ts","sourceRoot":"","sources":["../src/scheduler.interface.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;CAC3C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.stub.d.ts","sourceRoot":"","sources":["../src/scheduler.stub.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scheduler.stub.d.ts","sourceRoot":"","sources":["../src/scheduler.stub.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D,qBAAa,aAAc,YAAW,UAAU;IAC9C,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI;CAG5C"}
|
package/dist/scheduler.stub.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Executes additive schema DDL against the live database and introspects columns.
|
|
2
|
+
* Implemented by D1SchemaMutator in apps/api/src/shared/schema-mutator.d1.ts.
|
|
3
|
+
* This is the ONLY sanctioned channel for runtime DDL — handlers never touch env.DB. */
|
|
4
|
+
export interface ISchemaMutator {
|
|
5
|
+
/** Column names currently on a table, or null if the table does not exist. */
|
|
6
|
+
getColumns(table: string): Promise<Set<string> | null>;
|
|
7
|
+
/** Runs the given DDL statements in order as a single D1 batch.
|
|
8
|
+
* All statements must be additive (CREATE … IF NOT EXISTS / ALTER … ADD COLUMN /
|
|
9
|
+
* CREATE INDEX IF NOT EXISTS). Throws on the first failing statement. */
|
|
10
|
+
execDdl(statements: string[]): Promise<void>;
|
|
11
|
+
/** `DROP TABLE IF EXISTS {table}`. */
|
|
12
|
+
dropTable(table: string): Promise<void>;
|
|
13
|
+
/** `ALTER TABLE {table} DROP COLUMN {column}`. */
|
|
14
|
+
dropColumn(table: string, column: string): Promise<void>;
|
|
15
|
+
/** `ALTER TABLE {table} RENAME COLUMN {from} TO {to}`. */
|
|
16
|
+
renameColumn(table: string, from: string, to: string): Promise<void>;
|
|
17
|
+
/** Runs a multi-statement destructive batch atomically (FTS rebuild,
|
|
18
|
+
* type-change column rebuild, full type drop). The caller assembles the
|
|
19
|
+
* statements; the impl validates identifiers and runs them as one D1 batch. */
|
|
20
|
+
execDestructive(statements: string[]): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=schema-mutator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-mutator.d.ts","sourceRoot":"","sources":["../src/schema-mutator.ts"],"names":[],"mappings":"AAGA;;yFAEyF;AACzF,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAA;IACtD;;8EAE0E;IAC1E,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAS5C,sCAAsC;IACtC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,kDAAkD;IAClD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxD,0DAA0D;IAC1D,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpE;;oFAEgF;IAChF,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACrD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.repository.d.ts","sourceRoot":"","sources":["../../src/search/search.repository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"search.repository.d.ts","sourceRoot":"","sources":["../../src/search/search.repository.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;IAE9E;;;;OAIG;IACH,KAAK,CACH,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,QAAQ,CAAC,EACrD,KAAK,EAAE,IAAI,EAAE,GACZ,OAAO,CAAC,iBAAiB,CAAC,CAAA;CAC9B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Seed, BranchType } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ordered DROP TABLE statements for a full hard-delete of a seed's tables.
|
|
4
|
+
* Order: junction drafts → junctions → FTS triggers → FTS table → drafts → main table.
|
|
5
|
+
* All statements use IF EXISTS (idempotent).
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateDropTable(seed: Seed): string[];
|
|
8
|
+
/**
|
|
9
|
+
* DROP COLUMN statements for a single branch (by alias).
|
|
10
|
+
* For multi-relation branches: drops the junction tables (no column on parent).
|
|
11
|
+
* For regular branches: drops from main table + drafts table (if allowDrafts).
|
|
12
|
+
* Does NOT include FTS handling — callers must prepend ftsTriggerDrops() and
|
|
13
|
+
* append planFtsRebuild() when the branch is searchable.
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateDropColumn(seed: Seed, alias: string): string[];
|
|
16
|
+
/**
|
|
17
|
+
* RENAME COLUMN statements for a branch alias change.
|
|
18
|
+
* Updates main table + drafts (if allowDrafts).
|
|
19
|
+
* Does NOT handle FTS — callers must rebuild FTS separately (SQLite auto-updates
|
|
20
|
+
* trigger bodies on RENAME but the FTS virtual-table columns keep the old name).
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateRenameColumn(seed: Seed, fromAlias: string, toAlias: string): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Full FTS rebuild: drop triggers + FTS table, then recreate them, then backfill.
|
|
25
|
+
* Returns an empty array if the seed has no searchable branches.
|
|
26
|
+
* The drop steps use IF EXISTS so this is safe to call even if FTS was already dropped.
|
|
27
|
+
*/
|
|
28
|
+
export declare function planFtsRebuild(seed: Seed): string[];
|
|
29
|
+
export interface RetypeColumnPlan {
|
|
30
|
+
statements: string[];
|
|
31
|
+
ftsRebuildNeeded: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generates statements to change a branch's SQL storage type using the
|
|
35
|
+
* add-new-column / copy / drop-old / rename approach.
|
|
36
|
+
* Includes FTS rebuild statements when the branch is (or becomes) searchable.
|
|
37
|
+
* `updatedSeed` must already reflect the new type on the renamed branch.
|
|
38
|
+
*/
|
|
39
|
+
export declare function planRetypeColumn(seed: Seed, alias: string, newType: BranchType, updatedSeed: Seed): RetypeColumnPlan;
|
|
40
|
+
//# sourceMappingURL=seed-ddl-destructive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-ddl-destructive.d.ts","sourceRoot":"","sources":["../src/seed-ddl-destructive.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAU,UAAU,EAAE,MAAM,YAAY,CAAA;AAkC1D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAgBtD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBtE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ7F;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAiBnD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,IAAI,GAChB,gBAAgB,CAyClB"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
3
|
+
import { junctionTableName, generateFtsTable, generateFtsTriggers } from './engine.js';
|
|
4
|
+
const BRANCH_SQL_TYPE = {
|
|
5
|
+
text: 'TEXT',
|
|
6
|
+
number: 'REAL',
|
|
7
|
+
boolean: 'INTEGER',
|
|
8
|
+
date: 'INTEGER',
|
|
9
|
+
json: 'TEXT',
|
|
10
|
+
richtext: 'TEXT',
|
|
11
|
+
file: 'TEXT',
|
|
12
|
+
tags: 'TEXT',
|
|
13
|
+
relation: 'TEXT',
|
|
14
|
+
repeater: 'TEXT',
|
|
15
|
+
};
|
|
16
|
+
/** Branch types that receive a B-tree index in generateIndexes (used by retype to drop/recreate). */
|
|
17
|
+
const INDEXABLE_TYPES = new Set(['text', 'number', 'date', 'boolean', 'relation']);
|
|
18
|
+
function searchBranches(seed) {
|
|
19
|
+
return seed.branches.filter(b => (b.type === 'text' || b.type === 'richtext') && b.policies?.search !== false);
|
|
20
|
+
}
|
|
21
|
+
function ftsTriggerDrops(slug) {
|
|
22
|
+
return [
|
|
23
|
+
`DROP TRIGGER IF EXISTS fts_${slug}_insert;`,
|
|
24
|
+
`DROP TRIGGER IF EXISTS fts_${slug}_update;`,
|
|
25
|
+
`DROP TRIGGER IF EXISTS fts_${slug}_delete;`,
|
|
26
|
+
`DROP TABLE IF EXISTS fts_${slug};`,
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Ordered DROP TABLE statements for a full hard-delete of a seed's tables.
|
|
31
|
+
* Order: junction drafts → junctions → FTS triggers → FTS table → drafts → main table.
|
|
32
|
+
* All statements use IF EXISTS (idempotent).
|
|
33
|
+
*/
|
|
34
|
+
export function generateDropTable(seed) {
|
|
35
|
+
const stmts = [];
|
|
36
|
+
const { slug } = seed;
|
|
37
|
+
for (const branch of seed.branches) {
|
|
38
|
+
if (branch.type !== 'relation' || branch.multiple !== true)
|
|
39
|
+
continue;
|
|
40
|
+
const jt = junctionTableName(slug, branch.alias);
|
|
41
|
+
stmts.push(`DROP TABLE IF EXISTS ${jt}_drafts;`);
|
|
42
|
+
stmts.push(`DROP TABLE IF EXISTS ${jt};`);
|
|
43
|
+
}
|
|
44
|
+
stmts.push(...ftsTriggerDrops(slug));
|
|
45
|
+
stmts.push(`DROP TABLE IF EXISTS content_${slug}_drafts;`);
|
|
46
|
+
stmts.push(`DROP TABLE IF EXISTS content_${slug};`);
|
|
47
|
+
return stmts;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* DROP COLUMN statements for a single branch (by alias).
|
|
51
|
+
* For multi-relation branches: drops the junction tables (no column on parent).
|
|
52
|
+
* For regular branches: drops from main table + drafts table (if allowDrafts).
|
|
53
|
+
* Does NOT include FTS handling — callers must prepend ftsTriggerDrops() and
|
|
54
|
+
* append planFtsRebuild() when the branch is searchable.
|
|
55
|
+
*/
|
|
56
|
+
export function generateDropColumn(seed, alias) {
|
|
57
|
+
const stmts = [];
|
|
58
|
+
const { slug } = seed;
|
|
59
|
+
const branch = seed.branches.find(b => b.alias === alias);
|
|
60
|
+
if (!branch)
|
|
61
|
+
throw new Error(`Branch '${alias}' not found in seed '${slug}'`);
|
|
62
|
+
if (branch.type === 'relation' && branch.multiple === true) {
|
|
63
|
+
const jt = junctionTableName(slug, alias);
|
|
64
|
+
if (seed.allowDrafts)
|
|
65
|
+
stmts.push(`DROP TABLE IF EXISTS ${jt}_drafts;`);
|
|
66
|
+
stmts.push(`DROP TABLE IF EXISTS ${jt};`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
stmts.push(`ALTER TABLE content_${slug} DROP COLUMN ${alias};`);
|
|
70
|
+
if (seed.allowDrafts)
|
|
71
|
+
stmts.push(`ALTER TABLE content_${slug}_drafts DROP COLUMN ${alias};`);
|
|
72
|
+
}
|
|
73
|
+
return stmts;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* RENAME COLUMN statements for a branch alias change.
|
|
77
|
+
* Updates main table + drafts (if allowDrafts).
|
|
78
|
+
* Does NOT handle FTS — callers must rebuild FTS separately (SQLite auto-updates
|
|
79
|
+
* trigger bodies on RENAME but the FTS virtual-table columns keep the old name).
|
|
80
|
+
*/
|
|
81
|
+
export function generateRenameColumn(seed, fromAlias, toAlias) {
|
|
82
|
+
const stmts = [];
|
|
83
|
+
const { slug } = seed;
|
|
84
|
+
stmts.push(`ALTER TABLE content_${slug} RENAME COLUMN ${fromAlias} TO ${toAlias};`);
|
|
85
|
+
if (seed.allowDrafts) {
|
|
86
|
+
stmts.push(`ALTER TABLE content_${slug}_drafts RENAME COLUMN ${fromAlias} TO ${toAlias};`);
|
|
87
|
+
}
|
|
88
|
+
return stmts;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Full FTS rebuild: drop triggers + FTS table, then recreate them, then backfill.
|
|
92
|
+
* Returns an empty array if the seed has no searchable branches.
|
|
93
|
+
* The drop steps use IF EXISTS so this is safe to call even if FTS was already dropped.
|
|
94
|
+
*/
|
|
95
|
+
export function planFtsRebuild(seed) {
|
|
96
|
+
const rtBranches = searchBranches(seed);
|
|
97
|
+
if (rtBranches.length === 0)
|
|
98
|
+
return [];
|
|
99
|
+
const { slug } = seed;
|
|
100
|
+
const stmts = [...ftsTriggerDrops(slug)];
|
|
101
|
+
const ftsTable = generateFtsTable(seed);
|
|
102
|
+
if (ftsTable)
|
|
103
|
+
stmts.push(ftsTable);
|
|
104
|
+
stmts.push(...generateFtsTriggers(seed));
|
|
105
|
+
const cols = rtBranches.map(b => b.alias);
|
|
106
|
+
stmts.push(`INSERT INTO fts_${slug}(entry_id, ${cols.join(', ')}) SELECT id, ${cols.join(', ')} FROM content_${slug};`);
|
|
107
|
+
return stmts;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Generates statements to change a branch's SQL storage type using the
|
|
111
|
+
* add-new-column / copy / drop-old / rename approach.
|
|
112
|
+
* Includes FTS rebuild statements when the branch is (or becomes) searchable.
|
|
113
|
+
* `updatedSeed` must already reflect the new type on the renamed branch.
|
|
114
|
+
*/
|
|
115
|
+
export function planRetypeColumn(seed, alias, newType, updatedSeed) {
|
|
116
|
+
const branch = seed.branches.find(b => b.alias === alias);
|
|
117
|
+
if (!branch)
|
|
118
|
+
throw new Error(`Branch '${alias}' not found in seed '${seed.slug}'`);
|
|
119
|
+
const { slug } = seed;
|
|
120
|
+
const newSqlType = BRANCH_SQL_TYPE[newType];
|
|
121
|
+
const tmpAlias = `${alias}_migrate`;
|
|
122
|
+
const stmts = [];
|
|
123
|
+
// Always drop FTS first to avoid "column referenced in trigger" errors on DROP COLUMN.
|
|
124
|
+
stmts.push(...ftsTriggerDrops(slug));
|
|
125
|
+
// Drop the branch index if it exists on the old column (required before DROP COLUMN).
|
|
126
|
+
if (INDEXABLE_TYPES.has(branch.type) && branch.policies?.filter !== false) {
|
|
127
|
+
stmts.push(`DROP INDEX IF EXISTS idx_${slug}_${alias};`);
|
|
128
|
+
}
|
|
129
|
+
// Main table: add migration column, copy data, drop old, rename.
|
|
130
|
+
stmts.push(`ALTER TABLE content_${slug} ADD COLUMN ${tmpAlias} ${newSqlType};`);
|
|
131
|
+
stmts.push(`UPDATE content_${slug} SET ${tmpAlias} = CAST(${alias} AS ${newSqlType});`);
|
|
132
|
+
stmts.push(`ALTER TABLE content_${slug} DROP COLUMN ${alias};`);
|
|
133
|
+
stmts.push(`ALTER TABLE content_${slug} RENAME COLUMN ${tmpAlias} TO ${alias};`);
|
|
134
|
+
// Recreate index for the renamed column if the new type warrants it.
|
|
135
|
+
if (INDEXABLE_TYPES.has(newType) && branch.policies?.filter !== false) {
|
|
136
|
+
stmts.push(`CREATE INDEX IF NOT EXISTS idx_${slug}_${alias} ON content_${slug}(${alias});`);
|
|
137
|
+
}
|
|
138
|
+
// Drafts table (no indexes to worry about).
|
|
139
|
+
if (seed.allowDrafts) {
|
|
140
|
+
stmts.push(`ALTER TABLE content_${slug}_drafts ADD COLUMN ${tmpAlias} ${newSqlType};`);
|
|
141
|
+
stmts.push(`UPDATE content_${slug}_drafts SET ${tmpAlias} = CAST(${alias} AS ${newSqlType});`);
|
|
142
|
+
stmts.push(`ALTER TABLE content_${slug}_drafts DROP COLUMN ${alias};`);
|
|
143
|
+
stmts.push(`ALTER TABLE content_${slug}_drafts RENAME COLUMN ${tmpAlias} TO ${alias};`);
|
|
144
|
+
}
|
|
145
|
+
// Rebuild FTS if the seed has searchable branches in the updated definition.
|
|
146
|
+
const rebuild = planFtsRebuild(updatedSeed);
|
|
147
|
+
stmts.push(...rebuild);
|
|
148
|
+
return { statements: stmts, ftsRebuildNeeded: rebuild.length > 0 };
|
|
149
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Seed } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Full create-from-scratch statement set for a seed. Mirrors the CLI's buildStatements.
|
|
4
|
+
* Order: parent table → indexes → draft table → FTS table → FTS triggers →
|
|
5
|
+
* per multi-relation: junction table → junction indexes → junction draft table.
|
|
6
|
+
* Callers that create several seeds must order seeds with sortSeedsByDependencies
|
|
7
|
+
* first so relation FK targets exist.
|
|
8
|
+
*/
|
|
9
|
+
export declare function planCreateSeed(seed: Seed): string[];
|
|
10
|
+
export interface ExtendPlan {
|
|
11
|
+
statements: string[];
|
|
12
|
+
ftsRebuildNeeded: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Additive extension: given the columns that already exist on content_{slug}
|
|
16
|
+
* (from PRAGMA table_info, passed in by the caller), return ONLY the statements
|
|
17
|
+
* needed to add new branches — ADD COLUMN + indexes, plus junction tables for
|
|
18
|
+
* new multi-relation branches. Never drops or renames.
|
|
19
|
+
*
|
|
20
|
+
* FTS: SQLite cannot ALTER an fts5 table's columns. If a new text/richtext
|
|
21
|
+
* searchable branch was added, ftsRebuildNeeded=true signals the caller
|
|
22
|
+
* (sprint 03) to handle it — no DROP is emitted.
|
|
23
|
+
*/
|
|
24
|
+
export declare function planExtendSeed(seed: Seed, existingColumns: Set<string>): ExtendPlan;
|
|
25
|
+
/**
|
|
26
|
+
* Destructive FTS rebuild (sprint 06). SQLite cannot ALTER an fts5 table's
|
|
27
|
+
* columns, so when a searchable branch is added, renamed, retyped, or dropped
|
|
28
|
+
* the only correct fix is to drop and recreate the `fts_{slug}` virtual table
|
|
29
|
+
* (and its insert/update/delete triggers), then backfill from `content_{slug}`.
|
|
30
|
+
*
|
|
31
|
+
* Returns an empty array when the seed has no searchable branches — in that
|
|
32
|
+
* case the caller should instead drop any leftover FTS table via
|
|
33
|
+
* generateDropTable's FTS statements. The returned statements are destructive
|
|
34
|
+
* and must run through ISchemaMutator.execDestructive, never execDdl.
|
|
35
|
+
*
|
|
36
|
+
* Order: drop triggers → drop fts table → recreate table → recreate triggers →
|
|
37
|
+
* backfill existing rows.
|
|
38
|
+
*/
|
|
39
|
+
export declare function planFtsRebuild(seed: Seed): string[];
|
|
40
|
+
//# sourceMappingURL=seed-ddl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-ddl.d.ts","sourceRoot":"","sources":["../src/seed-ddl.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AActC;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAanD;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,UAAU,CAqBnF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CA0BnD"}
|
package/dist/seed-ddl.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2024–2026 Flavio De Musso
|
|
3
|
+
import { generateCreateTable, generateDraftTable, generateIndexes, generateFtsTable, generateFtsTriggers, generateAddColumn, generateJunctionTable, generateJunctionIndexes, generateJunctionDraftTable, indexableSearchBranches, } from './engine.js';
|
|
4
|
+
/**
|
|
5
|
+
* Full create-from-scratch statement set for a seed. Mirrors the CLI's buildStatements.
|
|
6
|
+
* Order: parent table → indexes → draft table → FTS table → FTS triggers →
|
|
7
|
+
* per multi-relation: junction table → junction indexes → junction draft table.
|
|
8
|
+
* Callers that create several seeds must order seeds with sortSeedsByDependencies
|
|
9
|
+
* first so relation FK targets exist.
|
|
10
|
+
*/
|
|
11
|
+
export function planCreateSeed(seed) {
|
|
12
|
+
const stmts = [generateCreateTable(seed), ...generateIndexes(seed)];
|
|
13
|
+
const draft = generateDraftTable(seed);
|
|
14
|
+
if (draft)
|
|
15
|
+
stmts.push(draft);
|
|
16
|
+
const fts = generateFtsTable(seed);
|
|
17
|
+
if (fts)
|
|
18
|
+
stmts.push(fts, ...generateFtsTriggers(seed));
|
|
19
|
+
for (const branch of seed.branches) {
|
|
20
|
+
if (branch.type !== 'relation' || branch.multiple !== true)
|
|
21
|
+
continue;
|
|
22
|
+
stmts.push(generateJunctionTable(seed, branch), ...generateJunctionIndexes(seed, branch));
|
|
23
|
+
const dj = generateJunctionDraftTable(seed, branch);
|
|
24
|
+
if (dj)
|
|
25
|
+
stmts.push(dj);
|
|
26
|
+
}
|
|
27
|
+
return stmts;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Additive extension: given the columns that already exist on content_{slug}
|
|
31
|
+
* (from PRAGMA table_info, passed in by the caller), return ONLY the statements
|
|
32
|
+
* needed to add new branches — ADD COLUMN + indexes, plus junction tables for
|
|
33
|
+
* new multi-relation branches. Never drops or renames.
|
|
34
|
+
*
|
|
35
|
+
* FTS: SQLite cannot ALTER an fts5 table's columns. If a new text/richtext
|
|
36
|
+
* searchable branch was added, ftsRebuildNeeded=true signals the caller
|
|
37
|
+
* (sprint 03) to handle it — no DROP is emitted.
|
|
38
|
+
*/
|
|
39
|
+
export function planExtendSeed(seed, existingColumns) {
|
|
40
|
+
const statements = [];
|
|
41
|
+
let ftsRebuildNeeded = false;
|
|
42
|
+
for (const branch of seed.branches) {
|
|
43
|
+
if (branch.type === 'relation' && branch.multiple === true) {
|
|
44
|
+
// junction table is CREATE IF NOT EXISTS — safe to re-emit
|
|
45
|
+
statements.push(generateJunctionTable(seed, branch), ...generateJunctionIndexes(seed, branch));
|
|
46
|
+
const dj = generateJunctionDraftTable(seed, branch);
|
|
47
|
+
if (dj)
|
|
48
|
+
statements.push(dj);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (existingColumns.has(branch.alias))
|
|
52
|
+
continue;
|
|
53
|
+
statements.push(generateAddColumn(seed, branch));
|
|
54
|
+
if ((branch.type === 'text' || branch.type === 'richtext') && branch.policies?.search !== false) {
|
|
55
|
+
ftsRebuildNeeded = true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// CREATE INDEX IF NOT EXISTS — idempotent, safe to re-run
|
|
59
|
+
statements.push(...generateIndexes(seed));
|
|
60
|
+
return { statements, ftsRebuildNeeded };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Destructive FTS rebuild (sprint 06). SQLite cannot ALTER an fts5 table's
|
|
64
|
+
* columns, so when a searchable branch is added, renamed, retyped, or dropped
|
|
65
|
+
* the only correct fix is to drop and recreate the `fts_{slug}` virtual table
|
|
66
|
+
* (and its insert/update/delete triggers), then backfill from `content_{slug}`.
|
|
67
|
+
*
|
|
68
|
+
* Returns an empty array when the seed has no searchable branches — in that
|
|
69
|
+
* case the caller should instead drop any leftover FTS table via
|
|
70
|
+
* generateDropTable's FTS statements. The returned statements are destructive
|
|
71
|
+
* and must run through ISchemaMutator.execDestructive, never execDdl.
|
|
72
|
+
*
|
|
73
|
+
* Order: drop triggers → drop fts table → recreate table → recreate triggers →
|
|
74
|
+
* backfill existing rows.
|
|
75
|
+
*/
|
|
76
|
+
export function planFtsRebuild(seed) {
|
|
77
|
+
const branches = indexableSearchBranches(seed);
|
|
78
|
+
if (branches.length === 0)
|
|
79
|
+
return [];
|
|
80
|
+
const slug = seed.slug;
|
|
81
|
+
const ftsTable = `fts_${slug}`;
|
|
82
|
+
const contentTable = `content_${slug}`;
|
|
83
|
+
const cols = branches.map(b => b.alias);
|
|
84
|
+
const ftsTableDdl = generateFtsTable(seed);
|
|
85
|
+
const triggers = generateFtsTriggers(seed);
|
|
86
|
+
// generateFtsTable/Triggers return non-null here (branches.length > 0 checked).
|
|
87
|
+
if (!ftsTableDdl)
|
|
88
|
+
return [];
|
|
89
|
+
const ftsColList = ['entry_id', ...cols].join(', ');
|
|
90
|
+
const selectList = ['id', ...cols].join(', ');
|
|
91
|
+
return [
|
|
92
|
+
`DROP TRIGGER IF EXISTS fts_${slug}_insert;`,
|
|
93
|
+
`DROP TRIGGER IF EXISTS fts_${slug}_update;`,
|
|
94
|
+
`DROP TRIGGER IF EXISTS fts_${slug}_delete;`,
|
|
95
|
+
`DROP TABLE IF EXISTS ${ftsTable};`,
|
|
96
|
+
ftsTableDdl,
|
|
97
|
+
...triggers,
|
|
98
|
+
`INSERT INTO ${ftsTable}(${ftsColList}) SELECT ${selectList} FROM ${contentTable};`,
|
|
99
|
+
];
|
|
100
|
+
}
|