@cogenta/schema 0.1.2 → 0.3.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/define-collection.d.ts.map +1 -1
- package/dist/define-collection.js +32 -0
- package/dist/define-collection.js.map +1 -1
- package/dist/define-taxonomy.d.ts +9 -0
- package/dist/define-taxonomy.d.ts.map +1 -0
- package/dist/define-taxonomy.js +144 -0
- package/dist/define-taxonomy.js.map +1 -0
- package/dist/fields.d.ts +20 -0
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +14 -0
- package/dist/fields.js.map +1 -1
- package/dist/generate-schema-json.d.ts +28 -4
- package/dist/generate-schema-json.d.ts.map +1 -1
- package/dist/generate-schema-json.js +25 -4
- package/dist/generate-schema-json.js.map +1 -1
- package/dist/generate-types.js +1 -0
- package/dist/generate-types.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/links/check.d.ts +76 -0
- package/dist/links/check.d.ts.map +1 -0
- package/dist/links/check.js +169 -0
- package/dist/links/check.js.map +1 -0
- package/dist/links/extract.d.ts +36 -0
- package/dist/links/extract.d.ts.map +1 -0
- package/dist/links/extract.js +69 -0
- package/dist/links/extract.js.map +1 -0
- package/dist/links/index.d.ts +10 -0
- package/dist/links/index.d.ts.map +1 -0
- package/dist/links/index.js +8 -0
- package/dist/links/index.js.map +1 -0
- package/dist/store/columns.d.ts.map +1 -1
- package/dist/store/columns.js +5 -2
- package/dist/store/columns.js.map +1 -1
- package/dist/store/index.d.ts +16 -2
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +9 -1
- package/dist/store/index.js.map +1 -1
- package/dist/store/lifecycle-events.d.ts +72 -0
- package/dist/store/lifecycle-events.d.ts.map +1 -0
- package/dist/store/lifecycle-events.js +73 -0
- package/dist/store/lifecycle-events.js.map +1 -0
- package/dist/store/menu-store.d.ts +99 -0
- package/dist/store/menu-store.d.ts.map +1 -0
- package/dist/store/menu-store.js +418 -0
- package/dist/store/menu-store.js.map +1 -0
- package/dist/store/menu-tables.d.ts +26 -0
- package/dist/store/menu-tables.d.ts.map +1 -0
- package/dist/store/menu-tables.js +79 -0
- package/dist/store/menu-tables.js.map +1 -0
- package/dist/store/naming.d.ts +11 -2
- package/dist/store/naming.d.ts.map +1 -1
- package/dist/store/naming.js +13 -1
- package/dist/store/naming.js.map +1 -1
- package/dist/store/read-only.d.ts +9 -3
- package/dist/store/read-only.d.ts.map +1 -1
- package/dist/store/read-only.js +13 -3
- package/dist/store/read-only.js.map +1 -1
- package/dist/store/scheduled-publish-enqueue.d.ts +37 -0
- package/dist/store/scheduled-publish-enqueue.d.ts.map +1 -0
- package/dist/store/scheduled-publish-enqueue.js +51 -0
- package/dist/store/scheduled-publish-enqueue.js.map +1 -0
- package/dist/store/schema-2-migration.d.ts +32 -0
- package/dist/store/schema-2-migration.d.ts.map +1 -0
- package/dist/store/schema-2-migration.js +136 -0
- package/dist/store/schema-2-migration.js.map +1 -0
- package/dist/store/search-indexing.d.ts +34 -0
- package/dist/store/search-indexing.d.ts.map +1 -0
- package/dist/store/search-indexing.js +65 -0
- package/dist/store/search-indexing.js.map +1 -0
- package/dist/store/store.d.ts +45 -8
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +398 -59
- package/dist/store/store.js.map +1 -1
- package/dist/store/tables.d.ts +15 -3
- package/dist/store/tables.d.ts.map +1 -1
- package/dist/store/tables.js +98 -6
- package/dist/store/tables.js.map +1 -1
- package/dist/store/taxonomy-path.d.ts +25 -0
- package/dist/store/taxonomy-path.d.ts.map +1 -0
- package/dist/store/taxonomy-path.js +64 -0
- package/dist/store/taxonomy-path.js.map +1 -0
- package/dist/store/taxonomy-store.d.ts +62 -0
- package/dist/store/taxonomy-store.d.ts.map +1 -0
- package/dist/store/taxonomy-store.js +292 -0
- package/dist/store/taxonomy-store.js.map +1 -0
- package/dist/store/types.d.ts +42 -2
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/values.d.ts.map +1 -1
- package/dist/store/values.js +4 -1
- package/dist/store/values.js.map +1 -1
- package/dist/system-fields.d.ts +2 -1
- package/dist/system-fields.d.ts.map +1 -1
- package/dist/system-fields.js +4 -0
- package/dist/system-fields.js.map +1 -1
- package/dist/types.d.ts +86 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +12 -3
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +5 -1
- package/dist/validation.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* Navigation menus.
|
|
4
|
+
*
|
|
5
|
+
* Unlike a taxonomy, a menu is not declared in the site's schema — it is
|
|
6
|
+
* created and edited entirely at runtime, from the admin or the API. So it
|
|
7
|
+
* gets one fixed pair of tables (`ensureMarketplaceTables`'s pattern), not one
|
|
8
|
+
* table per name the way `taxonomyTable()` mints one per declared taxonomy.
|
|
9
|
+
*
|
|
10
|
+
* A menu belongs to a locale, the way a localised collection does (ADR-0014):
|
|
11
|
+
* a site with French and English navigation has two rows in `cogenta_menus`
|
|
12
|
+
* sharing a `name`, not one row carrying two labels.
|
|
13
|
+
*
|
|
14
|
+
* An item's tree is a materialised path exactly like `taxonomy-path.ts`'s,
|
|
15
|
+
* reusing its helpers rather than a second implementation: a menu is
|
|
16
|
+
* structurally the same shape (a named tree), just editable without a schema
|
|
17
|
+
* declaration.
|
|
18
|
+
*/
|
|
19
|
+
export declare const MENU_TABLES: {
|
|
20
|
+
readonly menus: 'cogenta_menus';
|
|
21
|
+
readonly items: 'cogenta_menu_items';
|
|
22
|
+
};
|
|
23
|
+
/** Ids plus the deepest realistic nesting: `/`, then a UUID and a slash per level. */
|
|
24
|
+
export declare const MENU_ITEM_PATH_LENGTH: number;
|
|
25
|
+
export declare function ensureMenuTables(db: DatabaseHandle): Promise<void>;
|
|
26
|
+
//# sourceMappingURL=menu-tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-tables.d.ts","sourceRoot":"","sources":["../../src/store/menu-tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAqC,MAAM,eAAe,CAAA;AAItF;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,WAAW;aACtB,KAAK,EAAE,eAAe;aACtB,KAAK,EAAE,oBAAoB;CACnB,CAAA;AAEV,sFAAsF;AACtF,eAAO,MAAM,qBAAqB,QAAa,CAAA;AAE/C,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAuDxE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { identifier, sql } from '@cogenta/core';
|
|
2
|
+
import { integerColumn, jsonColumn, textColumn, timestampColumn, uuidColumn } from './columns.js';
|
|
3
|
+
import { indexName } from './naming.js';
|
|
4
|
+
/**
|
|
5
|
+
* Navigation menus.
|
|
6
|
+
*
|
|
7
|
+
* Unlike a taxonomy, a menu is not declared in the site's schema — it is
|
|
8
|
+
* created and edited entirely at runtime, from the admin or the API. So it
|
|
9
|
+
* gets one fixed pair of tables (`ensureMarketplaceTables`'s pattern), not one
|
|
10
|
+
* table per name the way `taxonomyTable()` mints one per declared taxonomy.
|
|
11
|
+
*
|
|
12
|
+
* A menu belongs to a locale, the way a localised collection does (ADR-0014):
|
|
13
|
+
* a site with French and English navigation has two rows in `cogenta_menus`
|
|
14
|
+
* sharing a `name`, not one row carrying two labels.
|
|
15
|
+
*
|
|
16
|
+
* An item's tree is a materialised path exactly like `taxonomy-path.ts`'s,
|
|
17
|
+
* reusing its helpers rather than a second implementation: a menu is
|
|
18
|
+
* structurally the same shape (a named tree), just editable without a schema
|
|
19
|
+
* declaration.
|
|
20
|
+
*/
|
|
21
|
+
export const MENU_TABLES = {
|
|
22
|
+
menus: 'cogenta_menus',
|
|
23
|
+
items: 'cogenta_menu_items',
|
|
24
|
+
};
|
|
25
|
+
/** Ids plus the deepest realistic nesting: `/`, then a UUID and a slash per level. */
|
|
26
|
+
export const MENU_ITEM_PATH_LENGTH = 37 * 8 + 1;
|
|
27
|
+
export async function ensureMenuTables(db) {
|
|
28
|
+
const dialect = db.dialect;
|
|
29
|
+
const menus = identifier(MENU_TABLES.menus, dialect);
|
|
30
|
+
const items = identifier(MENU_TABLES.items, dialect);
|
|
31
|
+
const menuStatements = [
|
|
32
|
+
sql `create table if not exists ${menus} (
|
|
33
|
+
${identifier('id', dialect)} ${uuidColumn(dialect)} not null primary key,
|
|
34
|
+
${identifier('name', dialect)} ${textColumn(dialect, 128)} not null,
|
|
35
|
+
${identifier('locale', dialect)} ${textColumn(dialect, 16)} not null,
|
|
36
|
+
${identifier('label', dialect)} ${textColumn(dialect, 255)} not null,
|
|
37
|
+
${identifier('created_at', dialect)} ${timestampColumn(dialect)} not null,
|
|
38
|
+
${identifier('updated_at', dialect)} ${timestampColumn(dialect)} not null
|
|
39
|
+
)`,
|
|
40
|
+
sql `create table if not exists ${items} (
|
|
41
|
+
${identifier('id', dialect)} ${uuidColumn(dialect)} not null primary key,
|
|
42
|
+
${identifier('menu_id', dialect)} ${uuidColumn(dialect)} not null,
|
|
43
|
+
${identifier('parent_id', dialect)} ${uuidColumn(dialect)},
|
|
44
|
+
${identifier('label', dialect)} ${textColumn(dialect, 255)} not null,
|
|
45
|
+
${identifier('kind', dialect)} ${textColumn(dialect, 32)} not null,
|
|
46
|
+
${identifier('target_collection', dialect)} ${textColumn(dialect, 128)},
|
|
47
|
+
${identifier('target_entry_id', dialect)} ${uuidColumn(dialect)},
|
|
48
|
+
${identifier('url', dialect)} ${textColumn(dialect, 2048)},
|
|
49
|
+
${identifier('position', dialect)} ${integerColumn()} not null,
|
|
50
|
+
${identifier('path', dialect)} ${textColumn(dialect, MENU_ITEM_PATH_LENGTH)} not null,
|
|
51
|
+
${identifier('open_in_new_tab', dialect)} ${jsonColumn()} not null,
|
|
52
|
+
${identifier('created_at', dialect)} ${timestampColumn(dialect)} not null,
|
|
53
|
+
${identifier('updated_at', dialect)} ${timestampColumn(dialect)} not null,
|
|
54
|
+
constraint ${identifier(indexName(MENU_TABLES.items, 'menu_fk'), dialect)}
|
|
55
|
+
foreign key (${identifier('menu_id', dialect)})
|
|
56
|
+
references ${menus} (${identifier('id', dialect)}) on delete cascade,
|
|
57
|
+
constraint ${identifier(indexName(MENU_TABLES.items, 'parent_fk'), dialect)}
|
|
58
|
+
foreign key (${identifier('parent_id', dialect)})
|
|
59
|
+
references ${items} (${identifier('id', dialect)}) on delete cascade
|
|
60
|
+
)`,
|
|
61
|
+
];
|
|
62
|
+
for (const statement of menuStatements) {
|
|
63
|
+
await db.query(statement);
|
|
64
|
+
}
|
|
65
|
+
const indexStatements = [
|
|
66
|
+
// A menu name is unique per locale: French and English navigation can
|
|
67
|
+
// both be called "main" without colliding.
|
|
68
|
+
sql `create unique index ${identifier(indexName(MENU_TABLES.menus, 'name_locale_unique'), dialect)}
|
|
69
|
+
on ${menus} (${identifier('name', dialect)}, ${identifier('locale', dialect)})`,
|
|
70
|
+
sql `create index ${identifier(indexName(MENU_TABLES.items, 'menu_path'), dialect)}
|
|
71
|
+
on ${items} (${identifier('menu_id', dialect)}, ${identifier('path', dialect)})`,
|
|
72
|
+
sql `create index ${identifier(indexName(MENU_TABLES.items, 'parent'), dialect)}
|
|
73
|
+
on ${items} (${identifier('parent_id', dialect)}, ${identifier('position', dialect)})`,
|
|
74
|
+
];
|
|
75
|
+
for (const statement of indexStatements) {
|
|
76
|
+
await db.query(statement).catch(() => undefined);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=menu-tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-tables.js","sourceRoot":"","sources":["../../src/store/menu-tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,UAAU,EAAoB,GAAG,EAAE,MAAM,eAAe,CAAA;AACtF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,oBAAoB;CACnB,CAAA;AAEV,sFAAsF;AACtF,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;AAE/C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAkB;IACvD,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAA;IAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IACpD,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAEpD,MAAM,cAAc,GAAkB;QACpC,GAAG,CAAA,8BAA8B,KAAK;QAClC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;QAChD,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC;QACvD,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACxD,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC;QACxD,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;QAC7D,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;MAC/D;QACF,GAAG,CAAA,8BAA8B,KAAK;QAClC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;QAChD,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;QACrD,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;QACvD,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC;QACxD,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACtD,UAAU,CAAC,mBAAmB,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC;QACpE,UAAU,CAAC,iBAAiB,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;QAC7D,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC;QACvD,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,aAAa,EAAE;QAClD,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,qBAAqB,CAAC;QACzE,UAAU,CAAC,iBAAiB,EAAE,OAAO,CAAC,IAAI,UAAU,EAAE;QACtD,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;QAC7D,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;mBAClD,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC;uBACxD,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC;qBAChC,KAAK,KAAK,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC;mBACrC,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC;uBAC1D,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC;qBAClC,KAAK,KAAK,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC;MAClD;KACH,CAAA;IAED,KAAK,MAAM,SAAS,IAAI,cAAc,EAAE,CAAC;QACvC,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAC3B,CAAC;IAED,MAAM,eAAe,GAAkB;QACrC,sEAAsE;QACtE,2CAA2C;QAC3C,GAAG,CAAA,uBAAuB,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,oBAAoB,CAAC,EAAE,OAAO,CAAC;aACxF,KAAK,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG;QACnF,GAAG,CAAA,gBAAgB,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC;aACxE,KAAK,KAAK,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QACpF,GAAG,CAAA,gBAAgB,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;aACrE,KAAK,KAAK,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG;KAC3F,CAAA;IAED,KAAK,MAAM,SAAS,IAAI,eAAe,EAAE,CAAC;QACxC,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAClD,CAAC;AACH,CAAC"}
|
package/dist/store/naming.d.ts
CHANGED
|
@@ -10,8 +10,17 @@ export declare function entriesTable(collection: string): string;
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function blocksTable(collection: string): string;
|
|
12
12
|
export declare function versionsTable(collection: string): string;
|
|
13
|
-
/** The join table of a `many: true` relation. */
|
|
13
|
+
/** The join table of a `many: true` relation, or of a `taxonomy` field. */
|
|
14
14
|
export declare function relationTable(collection: string, field: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* The terms of a taxonomy (`schema@2.0`, ADR-0022).
|
|
17
|
+
*
|
|
18
|
+
* A prefix of its own rather than `entriesTable`: a taxonomy and a collection
|
|
19
|
+
* may legitimately share a name — a site can have a `category` collection and
|
|
20
|
+
* a `category` taxonomy — and two different things must never land on one
|
|
21
|
+
* table.
|
|
22
|
+
*/
|
|
23
|
+
export declare function taxonomyTable(taxonomy: string): string;
|
|
15
24
|
export declare function indexName(table: string, suffix: string): string;
|
|
16
25
|
export declare function columnFor(field: string): string;
|
|
17
26
|
/**
|
|
@@ -19,7 +28,7 @@ export declare function columnFor(field: string): string;
|
|
|
19
28
|
* rather than silently shadowed — `updated_at` written by an editor would break
|
|
20
29
|
* every ordering and every cache decision in the product.
|
|
21
30
|
*/
|
|
22
|
-
export declare const SYSTEM_COLUMNS: readonly ['id', 'created_at', 'updated_at', 'created_by', 'updated_by', 'status', 'locale', 'translation_of', 'version', 'provenance', 'provenance_detail'];
|
|
31
|
+
export declare const SYSTEM_COLUMNS: readonly ['id', 'created_at', 'updated_at', 'created_by', 'updated_by', 'status', 'deleted_at', 'locale', 'translation_of', 'version', 'provenance', 'provenance_detail'];
|
|
23
32
|
export type SystemColumn = (typeof SYSTEM_COLUMNS)[number];
|
|
24
33
|
export declare function isSystemColumn(column: string): column is SystemColumn;
|
|
25
34
|
//# sourceMappingURL=naming.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../src/store/naming.ts"],"names":[],"mappings":"AAiCA,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,
|
|
1
|
+
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../src/store/naming.ts"],"names":[],"mappings":"AAiCA,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,YACzB,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,mBAAmB,CACX,CAAA;AAEV,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1D,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,YAAY,CAErE"}
|
package/dist/store/naming.js
CHANGED
|
@@ -46,10 +46,21 @@ export function blocksTable(collection) {
|
|
|
46
46
|
export function versionsTable(collection) {
|
|
47
47
|
return checked(`${entriesTable(collection)}_versions`);
|
|
48
48
|
}
|
|
49
|
-
/** The join table of a `many: true` relation. */
|
|
49
|
+
/** The join table of a `many: true` relation, or of a `taxonomy` field. */
|
|
50
50
|
export function relationTable(collection, field) {
|
|
51
51
|
return checked(`${entriesTable(collection)}_${toSnakeCase(field)}`);
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* The terms of a taxonomy (`schema@2.0`, ADR-0022).
|
|
55
|
+
*
|
|
56
|
+
* A prefix of its own rather than `entriesTable`: a taxonomy and a collection
|
|
57
|
+
* may legitimately share a name — a site can have a `category` collection and
|
|
58
|
+
* a `category` taxonomy — and two different things must never land on one
|
|
59
|
+
* table.
|
|
60
|
+
*/
|
|
61
|
+
export function taxonomyTable(taxonomy) {
|
|
62
|
+
return checked(`${PREFIX}tax_${toSnakeCase(taxonomy)}`);
|
|
63
|
+
}
|
|
53
64
|
export function indexName(table, suffix) {
|
|
54
65
|
return checked(`${table}_${suffix}`);
|
|
55
66
|
}
|
|
@@ -68,6 +79,7 @@ export const SYSTEM_COLUMNS = [
|
|
|
68
79
|
'created_by',
|
|
69
80
|
'updated_by',
|
|
70
81
|
'status',
|
|
82
|
+
'deleted_at',
|
|
71
83
|
'locale',
|
|
72
84
|
'translation_of',
|
|
73
85
|
'version',
|
package/dist/store/naming.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"naming.js","sourceRoot":"","sources":["../../src/store/naming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAE5C;;;;;;;GAOG;AAEH,MAAM,MAAM,GAAG,UAAU,CAAA;AAEzB;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG,EAAE,CAAA;AAEhC,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QACxC,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,iCAAiC,IAAI,QAAQ,IAAI,CAAC,MAAM,mBAAmB;YACpF,IAAI,EAAE,wBAAwB,qBAAqB,yCAAyC;YAC5F,OAAO,EAAE,EAAE,IAAI,EAAE;SAClB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,OAAO,OAAO,CAAC,GAAG,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;AACvD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,OAAO,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,OAAO,OAAO,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;AACxD,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"naming.js","sourceRoot":"","sources":["../../src/store/naming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAE5C;;;;;;;GAOG;AAEH,MAAM,MAAM,GAAG,UAAU,CAAA;AAEzB;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG,EAAE,CAAA;AAEhC,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QACxC,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,iCAAiC,IAAI,QAAQ,IAAI,CAAC,MAAM,mBAAmB;YACpF,IAAI,EAAE,wBAAwB,qBAAqB,yCAAyC;YAC5F,OAAO,EAAE,EAAE,IAAI,EAAE;SAClB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,UAAkB;IAC7C,OAAO,OAAO,CAAC,GAAG,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;AACvD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,OAAO,OAAO,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,OAAO,OAAO,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;AACxD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,aAAa,CAAC,UAAkB,EAAE,KAAa;IAC7D,OAAO,OAAO,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;AACrE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,OAAO,CAAC,GAAG,MAAM,OAAO,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;AACzD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,MAAc;IACrD,OAAO,OAAO,CAAC,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAA;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI;IACJ,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,QAAQ;IACR,YAAY;IACZ,QAAQ;IACR,gBAAgB;IAChB,SAAS;IACT,YAAY;IACZ,mBAAmB;CACX,CAAA;AAIV,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAQ,cAAoC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AAC/D,CAAC"}
|
|
@@ -3,9 +3,15 @@ import type { ContentValues } from './types.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* "Commencer par une démo en lecture seule" (L9 tâche 12, playground). Wraps
|
|
5
5
|
* any `ContentStore` so every mutating method refuses instead of writing —
|
|
6
|
-
* `create`/`update`/`delete`/`
|
|
7
|
-
* read (`read`/`list`/`history
|
|
8
|
-
* `resolveLocale`) passes straight
|
|
6
|
+
* `create`/`duplicate`/`update`/`delete`/`untrash`/`purge`/`purgeExpired`/
|
|
7
|
+
* `publish`/`unpublish`/`restore` — while every read (`read`/`list`/`history`/
|
|
8
|
+
* `readVersion`/`diff`/`translations`/`resolveLocale`) passes straight
|
|
9
|
+
* through, unchanged.
|
|
10
|
+
*
|
|
11
|
+
* The three trash methods are refused for the reason `delete` always was, and
|
|
12
|
+
* `purge` most of all: a read-only demo that let a visitor empty the trash
|
|
13
|
+
* would destroy content irrecoverably, which is worse than the write it was
|
|
14
|
+
* meant to prevent (ADR-0022).
|
|
9
15
|
*
|
|
10
16
|
* A single wrap at the store level protects every consumer built on top of
|
|
11
17
|
* it — REST's `ContentService` and GraphQL's gateway both construct their
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-only.d.ts","sourceRoot":"","sources":["../../src/store/read-only.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C
|
|
1
|
+
{"version":3,"file":"read-only.d.ts","sourceRoot":"","sources":["../../src/store/read-only.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,SAAS,aAAa,GAAG,aAAa,EAC7E,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,GAC3B,YAAY,CAAC,OAAO,CAAC,CA0BvB"}
|
package/dist/store/read-only.js
CHANGED
|
@@ -2,9 +2,15 @@ import { CogentaError } from '@cogenta/core';
|
|
|
2
2
|
/**
|
|
3
3
|
* "Commencer par une démo en lecture seule" (L9 tâche 12, playground). Wraps
|
|
4
4
|
* any `ContentStore` so every mutating method refuses instead of writing —
|
|
5
|
-
* `create`/`update`/`delete`/`
|
|
6
|
-
* read (`read`/`list`/`history
|
|
7
|
-
* `resolveLocale`) passes straight
|
|
5
|
+
* `create`/`duplicate`/`update`/`delete`/`untrash`/`purge`/`purgeExpired`/
|
|
6
|
+
* `publish`/`unpublish`/`restore` — while every read (`read`/`list`/`history`/
|
|
7
|
+
* `readVersion`/`diff`/`translations`/`resolveLocale`) passes straight
|
|
8
|
+
* through, unchanged.
|
|
9
|
+
*
|
|
10
|
+
* The three trash methods are refused for the reason `delete` always was, and
|
|
11
|
+
* `purge` most of all: a read-only demo that let a visitor empty the trash
|
|
12
|
+
* would destroy content irrecoverably, which is worse than the write it was
|
|
13
|
+
* meant to prevent (ADR-0022).
|
|
8
14
|
*
|
|
9
15
|
* A single wrap at the store level protects every consumer built on top of
|
|
10
16
|
* it — REST's `ContentService` and GraphQL's gateway both construct their
|
|
@@ -26,8 +32,12 @@ export function withReadOnlyStore(store) {
|
|
|
26
32
|
return {
|
|
27
33
|
...store,
|
|
28
34
|
create: () => refuse(),
|
|
35
|
+
duplicate: () => refuse(),
|
|
29
36
|
update: () => refuse(),
|
|
30
37
|
delete: () => refuse(),
|
|
38
|
+
untrash: () => refuse(),
|
|
39
|
+
purge: () => refuse(),
|
|
40
|
+
purgeExpired: () => refuse(),
|
|
31
41
|
publish: () => refuse(),
|
|
32
42
|
unpublish: () => refuse(),
|
|
33
43
|
restore: () => refuse(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-only.js","sourceRoot":"","sources":["../../src/store/read-only.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAI5C
|
|
1
|
+
{"version":3,"file":"read-only.js","sourceRoot":"","sources":["../../src/store/read-only.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAI5C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAA4B;IAE5B,0EAA0E;IAC1E,wEAAwE;IACxE,wEAAwE;IACxE,8BAA8B;IAC9B,KAAK,UAAU,MAAM;QACnB,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,mBAAmB;YACzB,OAAO,EAAE,iEAAiE;YAC1E,IAAI,EAAE,uHAAuH;SAC9H,CAAC,CAAA;IACJ,CAAC;IAED,OAAO;QACL,GAAG,KAAK;QACR,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACtB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACzB,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACtB,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACtB,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACvB,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACrB,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACvB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACzB,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;KACxB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { QueueDriver } from '@cogenta/core';
|
|
2
|
+
import type { CollectionDefinition } from '../types.js';
|
|
3
|
+
import type { ContentStore } from './store.js';
|
|
4
|
+
import type { ContentValues } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Queues the real publication of an entry saved with `status: 'scheduled'`.
|
|
7
|
+
*
|
|
8
|
+
* `../scheduling/publish.ts` was complete and tested from L1 —
|
|
9
|
+
* `schedulePublication`, `registerScheduledPublishing`, the whole
|
|
10
|
+
* `QueueDriver`-based mechanism — but nothing anywhere in the repository ever
|
|
11
|
+
* called `schedulePublication`. An editor could set an entry to "scheduled"
|
|
12
|
+
* with a future date and nothing would ever happen: the admin showed a
|
|
13
|
+
* read-only badge, honest about not being wired to anything (L10 audit
|
|
14
|
+
* follow-up).
|
|
15
|
+
*
|
|
16
|
+
* Wrapping the *store* rather than a route is the same choice
|
|
17
|
+
* `withSearchIndexing`/`withLifecycleEvents` already make, for the same
|
|
18
|
+
* reason: REST and GraphQL are handed the same store instances by
|
|
19
|
+
* `serve.ts`, so one wrap covers both transports.
|
|
20
|
+
*
|
|
21
|
+
* This module only ever *enqueues*. The actual state change — moving the
|
|
22
|
+
* entry to `published` — happens in the handler `serve.ts` registers with
|
|
23
|
+
* `registerScheduledPublishing`, which re-reads the entry before publishing
|
|
24
|
+
* it: an entry edited back to `draft` before its hour comes must not publish
|
|
25
|
+
* just because an older job is still sitting in the queue. That re-check is
|
|
26
|
+
* what makes it safe to enqueue again on every save rather than tracking
|
|
27
|
+
* (and cancelling) a previous job id here — two jobs racing to publish the
|
|
28
|
+
* same already-published entry is a no-op, not a bug.
|
|
29
|
+
*/
|
|
30
|
+
export interface ScheduledPublishEnqueueOptions {
|
|
31
|
+
readonly collection: CollectionDefinition;
|
|
32
|
+
readonly queue: QueueDriver;
|
|
33
|
+
/** Called when the enqueue itself fails. Defaults to swallowing it. */
|
|
34
|
+
readonly onError?: (error: unknown) => void;
|
|
35
|
+
}
|
|
36
|
+
export declare function withScheduledPublishEnqueue<TValues extends ContentValues = ContentValues>(store: ContentStore<TValues>, options: ScheduledPublishEnqueueOptions): ContentStore<TValues>;
|
|
37
|
+
//# sourceMappingURL=scheduled-publish-enqueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-publish-enqueue.d.ts","sourceRoot":"","sources":["../../src/store/scheduled-publish-enqueue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,YAAY,CAAA;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C;AAwBD,wBAAgB,2BAA2B,CAAC,OAAO,SAAS,aAAa,GAAG,aAAa,EACvF,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,OAAO,EAAE,8BAA8B,GACtC,YAAY,CAAC,OAAO,CAAC,CA4BvB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { schedulePublication } from '../scheduling/publish.js';
|
|
2
|
+
async function enqueueIfScheduled(entry, options) {
|
|
3
|
+
if (entry.status !== 'scheduled')
|
|
4
|
+
return;
|
|
5
|
+
if (entry.publishedAt === null)
|
|
6
|
+
return;
|
|
7
|
+
const publishAt = Date.parse(entry.publishedAt);
|
|
8
|
+
if (!Number.isFinite(publishAt))
|
|
9
|
+
return;
|
|
10
|
+
try {
|
|
11
|
+
await schedulePublication(options.queue, {
|
|
12
|
+
collection: options.collection.name,
|
|
13
|
+
entryId: entry.id,
|
|
14
|
+
locale: entry.locale,
|
|
15
|
+
publishAt,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
options.onError?.(error);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function withScheduledPublishEnqueue(store, options) {
|
|
23
|
+
return {
|
|
24
|
+
...store,
|
|
25
|
+
create: async (input) => {
|
|
26
|
+
const created = await store.create(input);
|
|
27
|
+
await enqueueIfScheduled(created, options);
|
|
28
|
+
return created;
|
|
29
|
+
},
|
|
30
|
+
update: async (id, input) => {
|
|
31
|
+
const updated = await store.update(id, input);
|
|
32
|
+
await enqueueIfScheduled(updated, options);
|
|
33
|
+
return updated;
|
|
34
|
+
},
|
|
35
|
+
// The one route that actually moves an *existing* entry into
|
|
36
|
+
// `scheduled`: `update()` never changes `status` (contract A keeps that
|
|
37
|
+
// to `publish`/`unpublish`), so `unpublish(id, { status: 'scheduled',
|
|
38
|
+
// publishedAt })` is where a schedule set from the admin really lands.
|
|
39
|
+
unpublish: async (id, input) => {
|
|
40
|
+
const unpublished = await store.unpublish(id, input);
|
|
41
|
+
await enqueueIfScheduled(unpublished, options);
|
|
42
|
+
return unpublished;
|
|
43
|
+
},
|
|
44
|
+
restore: async (id, version, input) => {
|
|
45
|
+
const restored = await store.restore(id, version, input);
|
|
46
|
+
await enqueueIfScheduled(restored, options);
|
|
47
|
+
return restored;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=scheduled-publish-enqueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-publish-enqueue.js","sourceRoot":"","sources":["../../src/store/scheduled-publish-enqueue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAqC9D,KAAK,UAAU,kBAAkB,CAC/B,KAA4B,EAC5B,OAAuC;IAEvC,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW;QAAE,OAAM;IACxC,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI;QAAE,OAAM;IAEtC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAM;IAEvC,IAAI,CAAC;QACH,MAAM,mBAAmB,CAAC,OAAO,CAAC,KAAK,EAAE;YACvC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI;YACnC,OAAO,EAAE,KAAK,CAAC,EAAE;YACjB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,SAAS;SACV,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,KAA4B,EAC5B,OAAuC;IAEvC,OAAO;QACL,GAAG,KAAK;QACR,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACtB,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACzC,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC1C,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE;YAC1B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;YAC7C,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC1C,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,6DAA6D;QAC7D,wEAAwE;QACxE,sEAAsE;QACtE,uEAAuE;QACvE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE;YAC7B,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;YACpD,MAAM,kBAAkB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;YAC9C,OAAO,WAAW,CAAA;QACpB,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YACpC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;YACxD,MAAM,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;YAC3C,OAAO,QAAQ,CAAA;QACjB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Migration } from '@cogenta/core';
|
|
2
|
+
import type { CollectionDefinition, TaxonomyDefinition } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The `schema@1.0 → 2.0` migration (ADR-0022).
|
|
5
|
+
*
|
|
6
|
+
* It does three things, and each is reversible:
|
|
7
|
+
*
|
|
8
|
+
* 1. adds `deleted_at` to every collection's entry table — the trash;
|
|
9
|
+
* 2. creates one terms table per declared taxonomy;
|
|
10
|
+
* 3. creates the join table of every `f.taxonomy({ many: true })` field.
|
|
11
|
+
*
|
|
12
|
+
* **The `down` is a real data loss and says so.** Dropping `deleted_at`
|
|
13
|
+
* discards the trash: every entry sitting in it becomes live again with no
|
|
14
|
+
* record that it was ever thrown away, and dropping the terms tables discards
|
|
15
|
+
* the whole classification. The migration is therefore marked `destructive`,
|
|
16
|
+
* which makes the migrator demand an explicit confirmation *and* a verified
|
|
17
|
+
* backup before it will run in either direction. Marking only the rollback is
|
|
18
|
+
* not something `Migration` can express, and erring towards asking is the
|
|
19
|
+
* right way round for a flag whose whole job is to make someone stop.
|
|
20
|
+
*
|
|
21
|
+
* The project has no site in production today, so this moves no real data —
|
|
22
|
+
* it is written and tested as if it did, because the day it does is not the
|
|
23
|
+
* day to start.
|
|
24
|
+
*/
|
|
25
|
+
export interface Schema2MigrationOptions {
|
|
26
|
+
readonly collections: readonly CollectionDefinition[];
|
|
27
|
+
readonly taxonomies?: readonly TaxonomyDefinition[];
|
|
28
|
+
/** Overridable so a site can slot this into its own numbering. */
|
|
29
|
+
readonly id?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function schema2Migration(options: Schema2MigrationOptions): Migration;
|
|
32
|
+
//# sourceMappingURL=schema-2-migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-2-migration.d.ts","sourceRoot":"","sources":["../../src/store/schema-2-migration.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EAIf,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAM3E;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IACrD,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACnD,kEAAkE;IAClE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CACrB;AA0GD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,SAAS,CA8D5E"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { identifier, sql, } from '@cogenta/core';
|
|
2
|
+
import { integerColumn, jsonColumn, textColumn, timestampColumn, uuidColumn } from './columns.js';
|
|
3
|
+
import { entriesTable, indexName, relationTable, taxonomyTable } from './naming.js';
|
|
4
|
+
import { relationsOf } from './tables.js';
|
|
5
|
+
import { TAXONOMY_PATH_LENGTH } from './taxonomy-path.js';
|
|
6
|
+
const DEFAULT_ID = '0002_schema_2_trash_and_taxonomies';
|
|
7
|
+
/**
|
|
8
|
+
* `add column` and `drop column` exist on all three dialects — but SQLite only
|
|
9
|
+
* learnt `drop column` in 3.35 (2021) and refuses it on a column an index
|
|
10
|
+
* touches, which is why the index is dropped first on the way down.
|
|
11
|
+
*/
|
|
12
|
+
function addDeletedAt(collection, dialect) {
|
|
13
|
+
const table = identifier(entriesTable(collection.name), dialect);
|
|
14
|
+
return sql `alter table ${table}
|
|
15
|
+
add column ${identifier('deleted_at', dialect)} ${timestampColumn(dialect)}`;
|
|
16
|
+
}
|
|
17
|
+
function dropDeletedAt(collection, dialect) {
|
|
18
|
+
const table = identifier(entriesTable(collection.name), dialect);
|
|
19
|
+
return sql `alter table ${table} drop column ${identifier('deleted_at', dialect)}`;
|
|
20
|
+
}
|
|
21
|
+
function trashIndex(collection, dialect) {
|
|
22
|
+
const name = entriesTable(collection.name);
|
|
23
|
+
return sql `create index ${identifier(indexName(name, 'trash'), dialect)}
|
|
24
|
+
on ${identifier(name, dialect)} (${identifier('deleted_at', dialect)})`;
|
|
25
|
+
}
|
|
26
|
+
function dropTrashIndex(collection, dialect) {
|
|
27
|
+
// MySQL has no `drop index if exists` before 8.0.x either, so the caller
|
|
28
|
+
// swallows the failure: the only realistic cause is that it is already gone.
|
|
29
|
+
return sql `drop index ${identifier(indexName(entriesTable(collection.name), 'trash'), dialect)}
|
|
30
|
+
on ${identifier(entriesTable(collection.name), dialect)}`;
|
|
31
|
+
}
|
|
32
|
+
function dropIndexStandalone(collection, dialect) {
|
|
33
|
+
return sql `drop index ${identifier(indexName(entriesTable(collection.name), 'trash'), dialect)}`;
|
|
34
|
+
}
|
|
35
|
+
function termsTable(taxonomy, dialect) {
|
|
36
|
+
const table = identifier(taxonomyTable(taxonomy.name), dialect);
|
|
37
|
+
return sql `create table if not exists ${table} (
|
|
38
|
+
${identifier('id', dialect)} ${uuidColumn(dialect)} not null primary key,
|
|
39
|
+
${identifier('parent_id', dialect)} ${uuidColumn(dialect)},
|
|
40
|
+
${identifier('slug', dialect)} ${textColumn(dialect, 255)} not null,
|
|
41
|
+
${identifier('labels', dialect)} ${jsonColumn()} not null,
|
|
42
|
+
${identifier('position', dialect)} ${integerColumn()} not null,
|
|
43
|
+
${identifier('path', dialect)} ${textColumn(dialect, TAXONOMY_PATH_LENGTH)} not null,
|
|
44
|
+
${identifier('created_at', dialect)} ${timestampColumn(dialect)} not null,
|
|
45
|
+
${identifier('updated_at', dialect)} ${timestampColumn(dialect)} not null,
|
|
46
|
+
constraint ${identifier(indexName(taxonomyTable(taxonomy.name), 'parent_fk'), dialect)}
|
|
47
|
+
foreign key (${identifier('parent_id', dialect)})
|
|
48
|
+
references ${table} (${identifier('id', dialect)}) on delete cascade
|
|
49
|
+
)`;
|
|
50
|
+
}
|
|
51
|
+
function termsIndexes(taxonomy, dialect) {
|
|
52
|
+
const name = taxonomyTable(taxonomy.name);
|
|
53
|
+
const table = identifier(name, dialect);
|
|
54
|
+
return [
|
|
55
|
+
sql `create unique index ${identifier(indexName(name, 'slug_unique'), dialect)}
|
|
56
|
+
on ${table} (${identifier('slug', dialect)})`,
|
|
57
|
+
sql `create index ${identifier(indexName(name, 'path'), dialect)}
|
|
58
|
+
on ${table} (${identifier('path', dialect)})`,
|
|
59
|
+
sql `create index ${identifier(indexName(name, 'parent'), dialect)}
|
|
60
|
+
on ${table} (${identifier('parent_id', dialect)}, ${identifier('position', dialect)})`,
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
function taxonomyJoinTable(collection, field, taxonomy, dialect) {
|
|
64
|
+
const name = relationTable(collection.name, field);
|
|
65
|
+
const table = identifier(name, dialect);
|
|
66
|
+
return sql `create table if not exists ${table} (
|
|
67
|
+
${identifier('entry_id', dialect)} ${uuidColumn(dialect)} not null,
|
|
68
|
+
${identifier('target_id', dialect)} ${uuidColumn(dialect)} not null,
|
|
69
|
+
${identifier('position', dialect)} ${integerColumn()} not null,
|
|
70
|
+
primary key (${identifier('entry_id', dialect)}, ${identifier('target_id', dialect)}),
|
|
71
|
+
constraint ${identifier(indexName(name, 'entry_fk'), dialect)}
|
|
72
|
+
foreign key (${identifier('entry_id', dialect)})
|
|
73
|
+
references ${identifier(entriesTable(collection.name), dialect)} (${identifier('id', dialect)})
|
|
74
|
+
on delete cascade,
|
|
75
|
+
constraint ${identifier(indexName(name, 'target_fk'), dialect)}
|
|
76
|
+
foreign key (${identifier('target_id', dialect)})
|
|
77
|
+
references ${identifier(taxonomyTable(taxonomy), dialect)} (${identifier('id', dialect)})
|
|
78
|
+
on delete cascade
|
|
79
|
+
)`;
|
|
80
|
+
}
|
|
81
|
+
/** The `many: true` taxonomy fields of a collection, with their taxonomy. */
|
|
82
|
+
function taxonomyJoins(collection) {
|
|
83
|
+
return relationsOf(collection)
|
|
84
|
+
.filter((relation) => relation.kind === 'taxonomy' && relation.many)
|
|
85
|
+
.map((relation) => ({ field: relation.field, taxonomy: relation.to }));
|
|
86
|
+
}
|
|
87
|
+
export function schema2Migration(options) {
|
|
88
|
+
const { collections } = options;
|
|
89
|
+
const taxonomies = options.taxonomies ?? [];
|
|
90
|
+
return {
|
|
91
|
+
id: options.id ?? DEFAULT_ID,
|
|
92
|
+
name: 'schema@2.0 — trash and taxonomies',
|
|
93
|
+
destructive: true,
|
|
94
|
+
impact: 'Adds a nullable deleted_at column to every collection and creates the taxonomy tables. ' +
|
|
95
|
+
'Moves no existing row. The rollback drops deleted_at and the taxonomy tables, which ' +
|
|
96
|
+
'permanently discards everything in the trash and every classification: entries sitting ' +
|
|
97
|
+
'in the trash silently become live again, with no record that they were ever deleted.',
|
|
98
|
+
estimatedDurationMs: 500 * Math.max(1, collections.length + taxonomies.length),
|
|
99
|
+
up: async (tx) => {
|
|
100
|
+
const dialect = tx.dialect;
|
|
101
|
+
// Terms first: a join table below carries a real foreign key into them.
|
|
102
|
+
for (const taxonomy of taxonomies) {
|
|
103
|
+
await tx.query(termsTable(taxonomy, dialect));
|
|
104
|
+
for (const statement of termsIndexes(taxonomy, dialect)) {
|
|
105
|
+
await tx.query(statement).catch(() => undefined);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const collection of collections) {
|
|
109
|
+
await tx.query(addDeletedAt(collection, dialect));
|
|
110
|
+
// `create index` has no portable `if not exists`; a failure here means
|
|
111
|
+
// it is already there, which is the state we wanted anyway.
|
|
112
|
+
await tx.query(trashIndex(collection, dialect)).catch(() => undefined);
|
|
113
|
+
for (const join of taxonomyJoins(collection)) {
|
|
114
|
+
await tx.query(taxonomyJoinTable(collection, join.field, join.taxonomy, dialect));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
down: async (tx) => {
|
|
119
|
+
const dialect = tx.dialect;
|
|
120
|
+
for (const collection of [...collections].reverse()) {
|
|
121
|
+
for (const join of taxonomyJoins(collection)) {
|
|
122
|
+
await tx.query(sql `drop table if exists ${identifier(relationTable(collection.name, join.field), dialect)}`);
|
|
123
|
+
}
|
|
124
|
+
// SQLite refuses to drop a column an index still covers, and the two
|
|
125
|
+
// dialects spell "drop index" differently — MySQL needs the table.
|
|
126
|
+
const drop = dialect === 'mysql' ? dropTrashIndex : dropIndexStandalone;
|
|
127
|
+
await tx.query(drop(collection, dialect)).catch(() => undefined);
|
|
128
|
+
await tx.query(dropDeletedAt(collection, dialect));
|
|
129
|
+
}
|
|
130
|
+
for (const taxonomy of [...taxonomies].reverse()) {
|
|
131
|
+
await tx.query(sql `drop table if exists ${identifier(taxonomyTable(taxonomy.name), dialect)}`);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=schema-2-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-2-migration.js","sourceRoot":"","sources":["../../src/store/schema-2-migration.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EAIV,GAAG,GACJ,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACjG,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAgCzD,MAAM,UAAU,GAAG,oCAAoC,CAAA;AAEvD;;;;GAIG;AACH,SAAS,YAAY,CAAC,UAAgC,EAAE,OAAwB;IAC9E,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;IAChE,OAAO,GAAG,CAAA,eAAe,KAAK;0BACN,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC,EAAE,CAAA;AACzF,CAAC;AAED,SAAS,aAAa,CAAC,UAAgC,EAAE,OAAwB;IAC/E,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;IAChE,OAAO,GAAG,CAAA,eAAe,KAAK,gBAAgB,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,CAAA;AACnF,CAAC;AAED,SAAS,UAAU,CAAC,UAAgC,EAAE,OAAwB;IAC5E,MAAM,IAAI,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IAC1C,OAAO,GAAG,CAAA,gBAAgB,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC;kBACvD,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAA;AACpF,CAAC;AAED,SAAS,cAAc,CAAC,UAAgC,EAAE,OAAwB;IAChF,yEAAyE;IACzE,6EAA6E;IAC7E,OAAO,GAAG,CAAA,cAAc,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC;kBAC9E,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,CAAA;AACtE,CAAC;AAED,SAAS,mBAAmB,CAC1B,UAAgC,EAChC,OAAwB;IAExB,OAAO,GAAG,CAAA,cAAc,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,CAAA;AAClG,CAAC;AAED,SAAS,UAAU,CAAC,QAA4B,EAAE,OAAwB;IACxE,MAAM,KAAK,GAAG,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;IAE/D,OAAO,GAAG,CAAA,8BAA8B,KAAK;MACzC,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;MAChD,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;MACvD,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC;MACvD,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,UAAU,EAAE;MAC7C,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,aAAa,EAAE;MAClD,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,oBAAoB,CAAC;MACxE,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;MAC7D,UAAU,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC;iBAClD,UAAU,CAAC,SAAS,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC;qBACrE,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC;mBAClC,KAAK,KAAK,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC;IAClD,CAAA;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,QAA4B,EAAE,OAAwB;IAC1E,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAEvC,OAAO;QACL,GAAG,CAAA,uBAAuB,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,OAAO,CAAC;aACpE,KAAK,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QACjD,GAAG,CAAA,gBAAgB,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC;aACtD,KAAK,KAAK,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QACjD,GAAG,CAAA,gBAAgB,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;aACxD,KAAK,KAAK,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG;KAC3F,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CACxB,UAAgC,EAChC,KAAa,EACb,QAAgB,EAChB,OAAwB;IAExB,MAAM,IAAI,GAAG,aAAa,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAClD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAEvC,OAAO,GAAG,CAAA,8BAA8B,KAAK;MACzC,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;MACtD,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC;MACvD,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,aAAa,EAAE;mBACrC,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC;iBACtE,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC;qBAC5C,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC;mBACjC,UAAU,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC;;iBAElF,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC;qBAC7C,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC;mBAClC,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,KAAK,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC;;IAEzF,CAAA;AACJ,CAAC;AAED,6EAA6E;AAC7E,SAAS,aAAa,CACpB,UAAgC;IAEhC,OAAO,WAAW,CAAC,UAAU,CAAC;SAC3B,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,UAAU,IAAI,QAAQ,CAAC,IAAI,CAAC;SACnE,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;AAC1E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAA;IAC/B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAA;IAE3C,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,UAAU;QAC5B,IAAI,EAAE,mCAAmC;QACzC,WAAW,EAAE,IAAI;QACjB,MAAM,EACJ,yFAAyF;YACzF,sFAAsF;YACtF,yFAAyF;YACzF,sFAAsF;QACxF,mBAAmB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;QAE9E,EAAE,EAAE,KAAK,EAAE,EAAe,EAAiB,EAAE;YAC3C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAA;YAE1B,wEAAwE;YACxE,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;gBAClC,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;gBAC7C,KAAK,MAAM,SAAS,IAAI,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;oBACxD,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;gBAClD,CAAC;YACH,CAAC;YAED,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;gBACrC,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAA;gBACjD,uEAAuE;gBACvE,4DAA4D;gBAC5D,MAAM,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;gBAEtE,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC7C,MAAM,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;gBACnF,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,EAAE,KAAK,EAAE,EAAe,EAAiB,EAAE;YAC7C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAA;YAE1B,KAAK,MAAM,UAAU,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;gBACpD,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC7C,MAAM,EAAE,CAAC,KAAK,CACZ,GAAG,CAAA,wBAAwB,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,EAAE,CAC7F,CAAA;gBACH,CAAC;gBAED,qEAAqE;gBACrE,mEAAmE;gBACnE,MAAM,IAAI,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,mBAAmB,CAAA;gBACvE,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;gBAChE,MAAM,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAA;YACpD,CAAC;YAED,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;gBACjD,MAAM,EAAE,CAAC,KAAK,CACZ,GAAG,CAAA,wBAAwB,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,CAC/E,CAAA;YACH,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SearchDriver } from '../search/types.js';
|
|
2
|
+
import type { CollectionDefinition } from '../types.js';
|
|
3
|
+
import type { ContentStore } from './store.js';
|
|
4
|
+
import type { ContentValues } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Keeps the full-text index in step with the content, by wrapping the store.
|
|
7
|
+
*
|
|
8
|
+
* The search engine (`src/search/`) was complete and tested from L1 and had
|
|
9
|
+
* no writer: nothing anywhere in the repository ever called `index()`, so
|
|
10
|
+
* every search returned nothing however the query was written (L10 task 3).
|
|
11
|
+
*
|
|
12
|
+
* Wrapping the *store* rather than hooking each transport is the same choice
|
|
13
|
+
* `withReadOnlyStore` already makes, and for the same reason: REST's
|
|
14
|
+
* `ContentService` and GraphQL's `ContentGateway` are both handed the very
|
|
15
|
+
* same store instances by `serve.ts`, so one wrap covers both transports and
|
|
16
|
+
* neither can bypass it. A hook on a router would have left the other
|
|
17
|
+
* transport writing content the index never hears about.
|
|
18
|
+
*/
|
|
19
|
+
export interface SearchIndexingOptions {
|
|
20
|
+
readonly collection: CollectionDefinition;
|
|
21
|
+
readonly index: SearchDriver;
|
|
22
|
+
/**
|
|
23
|
+
* Called when indexing fails. Defaults to swallowing it.
|
|
24
|
+
*
|
|
25
|
+
* A failed index write must never fail the content write that triggered it:
|
|
26
|
+
* the index is derived data that can be rebuilt from the content at any
|
|
27
|
+
* time, and losing an editor's save because a search table was locked would
|
|
28
|
+
* be the wrong trade. Callers with a logger pass one so the failure is
|
|
29
|
+
* still visible rather than silent — `serve.ts` does.
|
|
30
|
+
*/
|
|
31
|
+
readonly onError?: (error: unknown) => void;
|
|
32
|
+
}
|
|
33
|
+
export declare function withSearchIndexing<TValues extends ContentValues = ContentValues>(store: ContentStore<TValues>, options: SearchIndexingOptions): ContentStore<TValues>;
|
|
34
|
+
//# sourceMappingURL=search-indexing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-indexing.d.ts","sourceRoot":"","sources":["../../src/store/search-indexing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C;AA0CD,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,aAAa,GAAG,aAAa,EAC9E,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAC5B,OAAO,EAAE,qBAAqB,GAC7B,YAAY,CAAC,OAAO,CAAC,CA2BvB"}
|