@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.
Files changed (105) hide show
  1. package/dist/define-collection.d.ts.map +1 -1
  2. package/dist/define-collection.js +32 -0
  3. package/dist/define-collection.js.map +1 -1
  4. package/dist/define-taxonomy.d.ts +9 -0
  5. package/dist/define-taxonomy.d.ts.map +1 -0
  6. package/dist/define-taxonomy.js +144 -0
  7. package/dist/define-taxonomy.js.map +1 -0
  8. package/dist/fields.d.ts +20 -0
  9. package/dist/fields.d.ts.map +1 -1
  10. package/dist/fields.js +14 -0
  11. package/dist/fields.js.map +1 -1
  12. package/dist/generate-schema-json.d.ts +28 -4
  13. package/dist/generate-schema-json.d.ts.map +1 -1
  14. package/dist/generate-schema-json.js +25 -4
  15. package/dist/generate-schema-json.js.map +1 -1
  16. package/dist/generate-types.js +1 -0
  17. package/dist/generate-types.js.map +1 -1
  18. package/dist/index.d.ts +4 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/links/check.d.ts +76 -0
  23. package/dist/links/check.d.ts.map +1 -0
  24. package/dist/links/check.js +169 -0
  25. package/dist/links/check.js.map +1 -0
  26. package/dist/links/extract.d.ts +36 -0
  27. package/dist/links/extract.d.ts.map +1 -0
  28. package/dist/links/extract.js +69 -0
  29. package/dist/links/extract.js.map +1 -0
  30. package/dist/links/index.d.ts +10 -0
  31. package/dist/links/index.d.ts.map +1 -0
  32. package/dist/links/index.js +8 -0
  33. package/dist/links/index.js.map +1 -0
  34. package/dist/store/columns.d.ts.map +1 -1
  35. package/dist/store/columns.js +5 -2
  36. package/dist/store/columns.js.map +1 -1
  37. package/dist/store/index.d.ts +16 -2
  38. package/dist/store/index.d.ts.map +1 -1
  39. package/dist/store/index.js +9 -1
  40. package/dist/store/index.js.map +1 -1
  41. package/dist/store/lifecycle-events.d.ts +72 -0
  42. package/dist/store/lifecycle-events.d.ts.map +1 -0
  43. package/dist/store/lifecycle-events.js +73 -0
  44. package/dist/store/lifecycle-events.js.map +1 -0
  45. package/dist/store/menu-store.d.ts +99 -0
  46. package/dist/store/menu-store.d.ts.map +1 -0
  47. package/dist/store/menu-store.js +418 -0
  48. package/dist/store/menu-store.js.map +1 -0
  49. package/dist/store/menu-tables.d.ts +26 -0
  50. package/dist/store/menu-tables.d.ts.map +1 -0
  51. package/dist/store/menu-tables.js +79 -0
  52. package/dist/store/menu-tables.js.map +1 -0
  53. package/dist/store/naming.d.ts +11 -2
  54. package/dist/store/naming.d.ts.map +1 -1
  55. package/dist/store/naming.js +13 -1
  56. package/dist/store/naming.js.map +1 -1
  57. package/dist/store/read-only.d.ts +9 -3
  58. package/dist/store/read-only.d.ts.map +1 -1
  59. package/dist/store/read-only.js +13 -3
  60. package/dist/store/read-only.js.map +1 -1
  61. package/dist/store/scheduled-publish-enqueue.d.ts +37 -0
  62. package/dist/store/scheduled-publish-enqueue.d.ts.map +1 -0
  63. package/dist/store/scheduled-publish-enqueue.js +51 -0
  64. package/dist/store/scheduled-publish-enqueue.js.map +1 -0
  65. package/dist/store/schema-2-migration.d.ts +32 -0
  66. package/dist/store/schema-2-migration.d.ts.map +1 -0
  67. package/dist/store/schema-2-migration.js +136 -0
  68. package/dist/store/schema-2-migration.js.map +1 -0
  69. package/dist/store/search-indexing.d.ts +34 -0
  70. package/dist/store/search-indexing.d.ts.map +1 -0
  71. package/dist/store/search-indexing.js +65 -0
  72. package/dist/store/search-indexing.js.map +1 -0
  73. package/dist/store/store.d.ts +45 -8
  74. package/dist/store/store.d.ts.map +1 -1
  75. package/dist/store/store.js +398 -59
  76. package/dist/store/store.js.map +1 -1
  77. package/dist/store/tables.d.ts +15 -3
  78. package/dist/store/tables.d.ts.map +1 -1
  79. package/dist/store/tables.js +98 -6
  80. package/dist/store/tables.js.map +1 -1
  81. package/dist/store/taxonomy-path.d.ts +25 -0
  82. package/dist/store/taxonomy-path.d.ts.map +1 -0
  83. package/dist/store/taxonomy-path.js +64 -0
  84. package/dist/store/taxonomy-path.js.map +1 -0
  85. package/dist/store/taxonomy-store.d.ts +62 -0
  86. package/dist/store/taxonomy-store.d.ts.map +1 -0
  87. package/dist/store/taxonomy-store.js +292 -0
  88. package/dist/store/taxonomy-store.js.map +1 -0
  89. package/dist/store/types.d.ts +42 -2
  90. package/dist/store/types.d.ts.map +1 -1
  91. package/dist/store/values.d.ts.map +1 -1
  92. package/dist/store/values.js +4 -1
  93. package/dist/store/values.js.map +1 -1
  94. package/dist/system-fields.d.ts +2 -1
  95. package/dist/system-fields.d.ts.map +1 -1
  96. package/dist/system-fields.js +4 -0
  97. package/dist/system-fields.js.map +1 -1
  98. package/dist/types.d.ts +86 -4
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/types.js +12 -3
  101. package/dist/types.js.map +1 -1
  102. package/dist/validation.d.ts.map +1 -1
  103. package/dist/validation.js +5 -1
  104. package/dist/validation.js.map +1 -1
  105. 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"}
@@ -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,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvE;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,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"}
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"}
@@ -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',
@@ -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,iDAAiD;AACjD,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,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,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"}
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`/`publish`/`unpublish`/`restore` — while every
7
- * read (`read`/`list`/`history`/`readVersion`/`diff`/`translations`/
8
- * `resolveLocale`) passes straight through, unchanged.
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;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,SAAS,aAAa,GAAG,aAAa,EAC7E,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,GAC3B,YAAY,CAAC,OAAO,CAAC,CAsBvB"}
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"}
@@ -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`/`publish`/`unpublish`/`restore` — while every
6
- * read (`read`/`list`/`history`/`readVersion`/`diff`/`translations`/
7
- * `resolveLocale`) passes straight through, unchanged.
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;;;;;;;;;;;GAWG;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,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACtB,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;QACtB,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"}
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":"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"}
@@ -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"}