@cogenta/schema 0.1.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 (169) hide show
  1. package/dist/define-collection.d.ts +8 -0
  2. package/dist/define-collection.d.ts.map +1 -0
  3. package/dist/define-collection.js +311 -0
  4. package/dist/define-collection.js.map +1 -0
  5. package/dist/errors.d.ts +16 -0
  6. package/dist/errors.d.ts.map +1 -0
  7. package/dist/errors.js +18 -0
  8. package/dist/errors.js.map +1 -0
  9. package/dist/fields.d.ts +117 -0
  10. package/dist/fields.d.ts.map +1 -0
  11. package/dist/fields.js +107 -0
  12. package/dist/fields.js.map +1 -0
  13. package/dist/generate-schema-json.d.ts +61 -0
  14. package/dist/generate-schema-json.d.ts.map +1 -0
  15. package/dist/generate-schema-json.js +128 -0
  16. package/dist/generate-schema-json.js.map +1 -0
  17. package/dist/generate-types.d.ts +5 -0
  18. package/dist/generate-types.d.ts.map +1 -0
  19. package/dist/generate-types.js +220 -0
  20. package/dist/generate-types.js.map +1 -0
  21. package/dist/id.d.ts +8 -0
  22. package/dist/id.d.ts.map +1 -0
  23. package/dist/id.js +8 -0
  24. package/dist/id.js.map +1 -0
  25. package/dist/index.d.ts +28 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +22 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/rich-text.d.ts +164 -0
  30. package/dist/rich-text.d.ts.map +1 -0
  31. package/dist/rich-text.js +97 -0
  32. package/dist/rich-text.js.map +1 -0
  33. package/dist/routing/ids.d.ts +18 -0
  34. package/dist/routing/ids.d.ts.map +1 -0
  35. package/dist/routing/ids.js +41 -0
  36. package/dist/routing/ids.js.map +1 -0
  37. package/dist/routing/index.d.ts +13 -0
  38. package/dist/routing/index.d.ts.map +1 -0
  39. package/dist/routing/index.js +7 -0
  40. package/dist/routing/index.js.map +1 -0
  41. package/dist/routing/redirects.d.ts +76 -0
  42. package/dist/routing/redirects.d.ts.map +1 -0
  43. package/dist/routing/redirects.js +237 -0
  44. package/dist/routing/redirects.js.map +1 -0
  45. package/dist/routing/resolve.d.ts +38 -0
  46. package/dist/routing/resolve.d.ts.map +1 -0
  47. package/dist/routing/resolve.js +23 -0
  48. package/dist/routing/resolve.js.map +1 -0
  49. package/dist/routing/router.d.ts +51 -0
  50. package/dist/routing/router.d.ts.map +1 -0
  51. package/dist/routing/router.js +132 -0
  52. package/dist/routing/router.js.map +1 -0
  53. package/dist/routing/slug-change.d.ts +39 -0
  54. package/dist/routing/slug-change.d.ts.map +1 -0
  55. package/dist/routing/slug-change.js +44 -0
  56. package/dist/routing/slug-change.js.map +1 -0
  57. package/dist/routing/slug.d.ts +82 -0
  58. package/dist/routing/slug.d.ts.map +1 -0
  59. package/dist/routing/slug.js +124 -0
  60. package/dist/routing/slug.js.map +1 -0
  61. package/dist/routing/slugify.d.ts +29 -0
  62. package/dist/routing/slugify.d.ts.map +1 -0
  63. package/dist/routing/slugify.js +104 -0
  64. package/dist/routing/slugify.js.map +1 -0
  65. package/dist/scheduling/index.d.ts +3 -0
  66. package/dist/scheduling/index.d.ts.map +1 -0
  67. package/dist/scheduling/index.js +2 -0
  68. package/dist/scheduling/index.js.map +1 -0
  69. package/dist/scheduling/publish.d.ts +71 -0
  70. package/dist/scheduling/publish.d.ts.map +1 -0
  71. package/dist/scheduling/publish.js +122 -0
  72. package/dist/scheduling/publish.js.map +1 -0
  73. package/dist/search/extract.d.ts +25 -0
  74. package/dist/search/extract.d.ts.map +1 -0
  75. package/dist/search/extract.js +188 -0
  76. package/dist/search/extract.js.map +1 -0
  77. package/dist/search/index.d.ts +36 -0
  78. package/dist/search/index.d.ts.map +1 -0
  79. package/dist/search/index.js +44 -0
  80. package/dist/search/index.js.map +1 -0
  81. package/dist/search/mysql.d.ts +7 -0
  82. package/dist/search/mysql.d.ts.map +1 -0
  83. package/dist/search/mysql.js +121 -0
  84. package/dist/search/mysql.js.map +1 -0
  85. package/dist/search/postgres.d.ts +9 -0
  86. package/dist/search/postgres.d.ts.map +1 -0
  87. package/dist/search/postgres.js +167 -0
  88. package/dist/search/postgres.js.map +1 -0
  89. package/dist/search/query.d.ts +35 -0
  90. package/dist/search/query.d.ts.map +1 -0
  91. package/dist/search/query.js +116 -0
  92. package/dist/search/query.js.map +1 -0
  93. package/dist/search/sqlite.d.ts +16 -0
  94. package/dist/search/sqlite.d.ts.map +1 -0
  95. package/dist/search/sqlite.js +166 -0
  96. package/dist/search/sqlite.js.map +1 -0
  97. package/dist/search/table.d.ts +49 -0
  98. package/dist/search/table.d.ts.map +1 -0
  99. package/dist/search/table.js +86 -0
  100. package/dist/search/table.js.map +1 -0
  101. package/dist/search/text.d.ts +42 -0
  102. package/dist/search/text.d.ts.map +1 -0
  103. package/dist/search/text.js +56 -0
  104. package/dist/search/text.js.map +1 -0
  105. package/dist/search/types.d.ts +98 -0
  106. package/dist/search/types.d.ts.map +1 -0
  107. package/dist/search/types.js +2 -0
  108. package/dist/search/types.js.map +1 -0
  109. package/dist/store/columns.d.ts +33 -0
  110. package/dist/store/columns.d.ts.map +1 -0
  111. package/dist/store/columns.js +113 -0
  112. package/dist/store/columns.js.map +1 -0
  113. package/dist/store/cursor.d.ts +23 -0
  114. package/dist/store/cursor.d.ts.map +1 -0
  115. package/dist/store/cursor.js +36 -0
  116. package/dist/store/cursor.js.map +1 -0
  117. package/dist/store/diff.d.ts +59 -0
  118. package/dist/store/diff.d.ts.map +1 -0
  119. package/dist/store/diff.js +124 -0
  120. package/dist/store/diff.js.map +1 -0
  121. package/dist/store/fragments.d.ts +13 -0
  122. package/dist/store/fragments.d.ts.map +1 -0
  123. package/dist/store/fragments.js +25 -0
  124. package/dist/store/fragments.js.map +1 -0
  125. package/dist/store/index.d.ts +22 -0
  126. package/dist/store/index.d.ts.map +1 -0
  127. package/dist/store/index.js +16 -0
  128. package/dist/store/index.js.map +1 -0
  129. package/dist/store/naming.d.ts +25 -0
  130. package/dist/store/naming.d.ts.map +1 -0
  131. package/dist/store/naming.js +80 -0
  132. package/dist/store/naming.js.map +1 -0
  133. package/dist/store/read-only.d.ts +16 -0
  134. package/dist/store/read-only.d.ts.map +1 -0
  135. package/dist/store/read-only.js +36 -0
  136. package/dist/store/read-only.js.map +1 -0
  137. package/dist/store/store.d.ts +52 -0
  138. package/dist/store/store.d.ts.map +1 -0
  139. package/dist/store/store.js +706 -0
  140. package/dist/store/store.js.map +1 -0
  141. package/dist/store/tables.d.ts +40 -0
  142. package/dist/store/tables.d.ts.map +1 -0
  143. package/dist/store/tables.js +267 -0
  144. package/dist/store/tables.js.map +1 -0
  145. package/dist/store/types.d.ts +105 -0
  146. package/dist/store/types.d.ts.map +1 -0
  147. package/dist/store/types.js +2 -0
  148. package/dist/store/types.js.map +1 -0
  149. package/dist/store/uuid.d.ts +6 -0
  150. package/dist/store/uuid.d.ts.map +1 -0
  151. package/dist/store/uuid.js +64 -0
  152. package/dist/store/uuid.js.map +1 -0
  153. package/dist/store/values.d.ts +40 -0
  154. package/dist/store/values.d.ts.map +1 -0
  155. package/dist/store/values.js +160 -0
  156. package/dist/store/values.js.map +1 -0
  157. package/dist/system-fields.d.ts +60 -0
  158. package/dist/system-fields.d.ts.map +1 -0
  159. package/dist/system-fields.js +73 -0
  160. package/dist/system-fields.js.map +1 -0
  161. package/dist/types.d.ts +101 -0
  162. package/dist/types.d.ts.map +1 -0
  163. package/dist/types.js +32 -0
  164. package/dist/types.js.map +1 -0
  165. package/dist/validation.d.ts +54 -0
  166. package/dist/validation.d.ts.map +1 -0
  167. package/dist/validation.js +183 -0
  168. package/dist/validation.js.map +1 -0
  169. package/package.json +43 -0
@@ -0,0 +1,98 @@
1
+ import type { DatabaseDialect, HealthReport } from '@cogenta/core';
2
+ import type { ContentStatus } from '../types.js';
3
+ /**
4
+ * One indexable unit: an entry, in one locale, in one state.
5
+ *
6
+ * The index holds *extracted text*, never the stored JSON. A `richText`
7
+ * document indexed raw would make `_type`, `span` and `markDefs` searchable
8
+ * terms, and would rank a page by how many spans it happens to be split into.
9
+ */
10
+ export interface SearchDocument {
11
+ readonly id: string;
12
+ readonly collection: string;
13
+ readonly locale: string;
14
+ readonly status: ContentStatus;
15
+ /** Short label, returned with the hit so a caller can render a result list. */
16
+ readonly title: string;
17
+ /** Everything else worth matching, already flattened to plain text. */
18
+ readonly body: string;
19
+ }
20
+ /** What identifies a document in the index. Entry ids are unique, but the
21
+ * collection is part of the key so that clearing one collection is possible. */
22
+ export interface SearchReference {
23
+ readonly id: string;
24
+ readonly collection: string;
25
+ }
26
+ /**
27
+ * A query.
28
+ *
29
+ * `locale` and `status` are **not optional filters**: they are the query.
30
+ * Section 5 of the architecture makes permission filtering at query time
31
+ * non-negotiable, and the way to honour that is to make it impossible to ask
32
+ * for "everything" — there is no value of `SearchQuery` that reaches a draft
33
+ * unless the caller names `draft`, and no value that crosses a locale.
34
+ */
35
+ export interface SearchQuery {
36
+ readonly text: string;
37
+ readonly locale: string;
38
+ /** Defaults to `published`: the safe answer when a caller says nothing. */
39
+ readonly status?: ContentStatus;
40
+ /** Restricts to these collections. Absent means every indexed collection. */
41
+ readonly collections?: readonly string[];
42
+ readonly limit?: number;
43
+ readonly offset?: number;
44
+ }
45
+ export interface SearchHit {
46
+ readonly id: string;
47
+ readonly collection: string;
48
+ readonly locale: string;
49
+ readonly status: ContentStatus;
50
+ readonly title: string;
51
+ /**
52
+ * Relevance, higher is better. Comparable **within one result set only**:
53
+ * a `ts_rank`, an InnoDB fulltext score and a BM25 are three different
54
+ * numbers, and the L1 spec accepts that rather than reimplementing an engine.
55
+ */
56
+ readonly score: number;
57
+ }
58
+ export interface SearchResults {
59
+ readonly hits: readonly SearchHit[];
60
+ readonly hasMore: boolean;
61
+ /**
62
+ * Offset of the next page, or null.
63
+ *
64
+ * Search is the one place the project pages by offset rather than by cursor.
65
+ * A keyset cursor needs a total order over stable column values; a relevance
66
+ * score is neither stable across an edit nor comparable between two runs of
67
+ * the query, so a cursor built on it would silently skip and repeat rows.
68
+ * The offset is capped instead, which is what a search UI needs anyway.
69
+ */
70
+ readonly nextOffset: number | null;
71
+ }
72
+ /**
73
+ * Full-text search, one interface over three engines that agree on very little.
74
+ *
75
+ * Postgres has `tsvector` and GIN, MySQL has `FULLTEXT` in boolean mode, SQLite
76
+ * has FTS5 — and, when the runtime was built without it, nothing at all. The
77
+ * L1 spec asks for exactly this: a common interface, and unequal result quality
78
+ * accepted rather than a search engine written from scratch. What every
79
+ * implementation *does* guarantee is the part that is not about ranking:
80
+ *
81
+ * - a document whose text contains every term of the query is found;
82
+ * - accents and case never change whether it is found;
83
+ * - a document in another locale, or in another state, is never returned.
84
+ */
85
+ export interface SearchDriver {
86
+ readonly dialect: DatabaseDialect;
87
+ /** Adds or replaces a document. Indexing the same reference twice is a
88
+ * rewrite, so re-indexing after an edit needs no prior delete. */
89
+ index(document: SearchDocument): Promise<void>;
90
+ remove(reference: SearchReference): Promise<void>;
91
+ search(query: SearchQuery): Promise<SearchResults>;
92
+ /** Empties the index, or only one collection's share of it. */
93
+ clear(scope?: {
94
+ readonly collection?: string;
95
+ }): Promise<void>;
96
+ health(): Promise<HealthReport>;
97
+ }
98
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/search/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;gFACgF;AAChF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAA;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;IACjC;sEACkE;IAClE,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,MAAM,CAAC,SAAS,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAClD,+DAA+D;IAC/D,KAAK,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9D,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC,CAAA;CAChC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/search/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,33 @@
1
+ import { type DatabaseDialect, type SqlFragment } from '@cogenta/core';
2
+ import type { FieldDefinition, OnDelete } from '../types.js';
3
+ /**
4
+ * The physical type of every field, per dialect.
5
+ *
6
+ * Two choices here are deliberate and worth stating, because both look wrong to
7
+ * someone who knows only one database:
8
+ *
9
+ * 1. **Identifiers are `uuid` on Postgres, `char(36)` on MySQL, `text` on
10
+ * SQLite** (ADR-0015). The application mints the value, so nothing depends on
11
+ * `RETURNING` or on `insertId`.
12
+ *
13
+ * 2. **Timestamps and JSON are stored as text on every dialect.** A `timestamptz`
14
+ * comes back from `pg` as a `Date` while SQLite returns a string, and a
15
+ * `jsonb` comes back parsed while MySQL returns a string — so a column typed
16
+ * "natively" would make a row mean something different per dialect, which is
17
+ * exactly the leak the db layer exists to prevent. Contract A already
18
+ * declares `createdAt` as a string; ISO-8601 in UTC also sorts
19
+ * lexicographically, which is what keyset pagination needs.
20
+ */
21
+ export declare function uuidColumn(dialect: DatabaseDialect): SqlFragment;
22
+ /** `varchar(n)` where it exists; SQLite has one string type and ignores the length. */
23
+ export declare function textColumn(dialect: DatabaseDialect, length: number): SqlFragment;
24
+ export declare function jsonColumn(): SqlFragment;
25
+ export declare function timestampColumn(dialect: DatabaseDialect): SqlFragment;
26
+ export declare function booleanColumn(dialect: DatabaseDialect): SqlFragment;
27
+ export declare function integerColumn(): SqlFragment;
28
+ /** Field kinds that get no column of their own on the entry table. */
29
+ export declare function isColumnless(field: FieldDefinition): boolean;
30
+ export declare function columnTypeFor(field: FieldDefinition, dialect: DatabaseDialect): SqlFragment;
31
+ /** `restrict` is the default on purpose: deleting an author must not erase articles. */
32
+ export declare function onDeleteClause(onDelete: OnDelete | undefined): SqlFragment;
33
+ //# sourceMappingURL=columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columns.d.ts","sourceRoot":"","sources":["../../src/store/columns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,WAAW,EAAa,MAAM,eAAe,CAAA;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE5D;;;;;;;;;;;;;;;;;GAiBG;AAEH,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAIhE;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAEhF;AAED,wBAAgB,UAAU,IAAI,WAAW,CAIxC;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAErE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAGnE;AAED,wBAAgB,aAAa,IAAI,WAAW,CAE3C;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAK5D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,eAAe,GAAG,WAAW,CA+C3F;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAK1E"}
@@ -0,0 +1,113 @@
1
+ import { CogentaError, unsafeRaw } from '@cogenta/core';
2
+ /**
3
+ * The physical type of every field, per dialect.
4
+ *
5
+ * Two choices here are deliberate and worth stating, because both look wrong to
6
+ * someone who knows only one database:
7
+ *
8
+ * 1. **Identifiers are `uuid` on Postgres, `char(36)` on MySQL, `text` on
9
+ * SQLite** (ADR-0015). The application mints the value, so nothing depends on
10
+ * `RETURNING` or on `insertId`.
11
+ *
12
+ * 2. **Timestamps and JSON are stored as text on every dialect.** A `timestamptz`
13
+ * comes back from `pg` as a `Date` while SQLite returns a string, and a
14
+ * `jsonb` comes back parsed while MySQL returns a string — so a column typed
15
+ * "natively" would make a row mean something different per dialect, which is
16
+ * exactly the leak the db layer exists to prevent. Contract A already
17
+ * declares `createdAt` as a string; ISO-8601 in UTC also sorts
18
+ * lexicographically, which is what keyset pagination needs.
19
+ */
20
+ export function uuidColumn(dialect) {
21
+ if (dialect === 'postgres')
22
+ return unsafeRaw('uuid');
23
+ if (dialect === 'mysql')
24
+ return unsafeRaw('char(36)');
25
+ return unsafeRaw('text');
26
+ }
27
+ /** `varchar(n)` where it exists; SQLite has one string type and ignores the length. */
28
+ export function textColumn(dialect, length) {
29
+ return unsafeRaw(dialect === 'sqlite' ? 'text' : `varchar(${length})`);
30
+ }
31
+ export function jsonColumn() {
32
+ // Text on every dialect, not `jsonb`/`json`: see the note above about drivers
33
+ // parsing one and not the other. Nothing in L1 queries inside the document.
34
+ return unsafeRaw('text');
35
+ }
36
+ export function timestampColumn(dialect) {
37
+ return textColumn(dialect, 32);
38
+ }
39
+ export function booleanColumn(dialect) {
40
+ if (dialect === 'postgres')
41
+ return unsafeRaw('boolean');
42
+ return unsafeRaw(dialect === 'mysql' ? 'tinyint' : 'integer');
43
+ }
44
+ export function integerColumn() {
45
+ return unsafeRaw('integer');
46
+ }
47
+ /** Field kinds that get no column of their own on the entry table. */
48
+ export function isColumnless(field) {
49
+ if (field.kind === 'blocks')
50
+ return true;
51
+ // A to-many relation lives in a join table, with a real foreign key on both
52
+ // sides (contract A). Only a to-one relation is a column.
53
+ return field.kind === 'relation' && field.options['many'] === true;
54
+ }
55
+ export function columnTypeFor(field, dialect) {
56
+ switch (field.kind) {
57
+ case 'text': {
58
+ const max = field.options['max'];
59
+ return typeof max === 'number' && max > 0 && max <= 65_535
60
+ ? textColumn(dialect, max)
61
+ : unsafeRaw('text');
62
+ }
63
+ case 'slug':
64
+ return textColumn(dialect, 255);
65
+ case 'select':
66
+ return textColumn(dialect, 255);
67
+ case 'color':
68
+ return textColumn(dialect, 32);
69
+ case 'number':
70
+ if (dialect === 'postgres')
71
+ return unsafeRaw('double precision');
72
+ return unsafeRaw(dialect === 'mysql' ? 'double' : 'real');
73
+ case 'boolean':
74
+ return booleanColumn(dialect);
75
+ case 'date':
76
+ return textColumn(dialect, 10);
77
+ case 'datetime':
78
+ return timestampColumn(dialect);
79
+ case 'media':
80
+ // No foreign key: the media library is its own subsystem and a content
81
+ // table must not refuse to be created because it is not installed yet.
82
+ return textColumn(dialect, 36);
83
+ case 'relation':
84
+ return uuidColumn(dialect);
85
+ case 'richText':
86
+ case 'json':
87
+ case 'geo':
88
+ return jsonColumn();
89
+ case 'blocks':
90
+ throw new CogentaError({
91
+ code: 'CONTENT_INVALID',
92
+ message: 'A blocks field has no column: each block is a row of its own.',
93
+ hint: 'This is a bug in the caller — blocks are stored in the <collection>_blocks table.',
94
+ });
95
+ default:
96
+ throw new CogentaError({
97
+ code: 'CONFIG_INVALID',
98
+ message: `Unknown field kind "${String(field.kind)}".`,
99
+ hint: 'Field kinds are the closed set declared by contract A.',
100
+ details: { kind: field.kind },
101
+ });
102
+ }
103
+ }
104
+ /** `restrict` is the default on purpose: deleting an author must not erase articles. */
105
+ export function onDeleteClause(onDelete) {
106
+ const action = onDelete ?? 'restrict';
107
+ if (action === 'cascade')
108
+ return unsafeRaw('on delete cascade');
109
+ if (action === 'setNull')
110
+ return unsafeRaw('on delete set null');
111
+ return unsafeRaw('on delete restrict');
112
+ }
113
+ //# sourceMappingURL=columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columns.js","sourceRoot":"","sources":["../../src/store/columns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { SortField, SortOrder } from './types.js';
2
+ /**
3
+ * Keyset pagination. Never an offset.
4
+ *
5
+ * `offset` counts rows from the start of the result on every request, so an
6
+ * insertion or a deletion anywhere before the window shifts everything after it:
7
+ * a reader paging through a live collection sees an entry twice, or never. The
8
+ * L1 spec calls this out and the acceptance criterion is explicit — "the cursor
9
+ * is stable during concurrent insertions".
10
+ *
11
+ * A cursor is therefore a *position in the ordering*, not a count: the sort
12
+ * value and the id of the last row handed out. The next page asks for rows
13
+ * strictly after that point, which no concurrent write can move.
14
+ */
15
+ export interface Cursor {
16
+ readonly field: SortField;
17
+ readonly direction: 'asc' | 'desc';
18
+ readonly value: string;
19
+ readonly id: string;
20
+ }
21
+ export declare function encodeCursor(cursor: Cursor): string;
22
+ export declare function decodeCursor(raw: string, expected: SortOrder): Cursor;
23
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/store/cursor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,MAAM,CAkCrE"}
@@ -0,0 +1,36 @@
1
+ import { CogentaError } from '@cogenta/core';
2
+ export function encodeCursor(cursor) {
3
+ return Buffer.from(JSON.stringify(cursor), 'utf8').toString('base64url');
4
+ }
5
+ export function decodeCursor(raw, expected) {
6
+ const invalid = (reason) => new CogentaError({
7
+ code: 'CONTENT_INVALID',
8
+ message: `This pagination cursor cannot be used: ${reason}.`,
9
+ hint: 'Pass the `nextCursor` of the previous page unchanged, with the same sort.',
10
+ });
11
+ let parsed;
12
+ try {
13
+ parsed = JSON.parse(Buffer.from(raw, 'base64url').toString('utf8'));
14
+ }
15
+ catch {
16
+ throw invalid('it is not a cursor this API produced');
17
+ }
18
+ if (typeof parsed !== 'object' || parsed === null)
19
+ throw invalid('it is not a cursor');
20
+ const candidate = parsed;
21
+ if (typeof candidate.value !== 'string' || typeof candidate.id !== 'string') {
22
+ throw invalid('it is missing its position');
23
+ }
24
+ // A cursor taken under one ordering means nothing under another: the same
25
+ // position would skip rows or repeat them. Refusing is the only safe answer.
26
+ if (candidate.field !== expected.field || candidate.direction !== expected.direction) {
27
+ throw invalid('it was produced with a different sort order');
28
+ }
29
+ return {
30
+ field: candidate.field,
31
+ direction: candidate.direction,
32
+ value: candidate.value,
33
+ id: candidate.id,
34
+ };
35
+ }
36
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/store/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAuB5C,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;AAC1E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,QAAmB;IAC3D,MAAM,OAAO,GAAG,CAAC,MAAc,EAAgB,EAAE,CAC/C,IAAI,YAAY,CAAC;QACf,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,0CAA0C,MAAM,GAAG;QAC5D,IAAI,EAAE,2EAA2E;KAClF,CAAC,CAAA;IAEJ,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,OAAO,CAAC,sCAAsC,CAAC,CAAA;IACvD,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACtF,MAAM,SAAS,GAAG,MAAyB,CAAA;IAE3C,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC5E,MAAM,OAAO,CAAC,4BAA4B,CAAC,CAAA;IAC7C,CAAC;IAED,0EAA0E;IAC1E,6EAA6E;IAC7E,IAAI,SAAS,CAAC,KAAK,KAAK,QAAQ,CAAC,KAAK,IAAI,SAAS,CAAC,SAAS,KAAK,QAAQ,CAAC,SAAS,EAAE,CAAC;QACrF,MAAM,OAAO,CAAC,6CAA6C,CAAC,CAAA;IAC9D,CAAC;IAED,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,SAAS,EAAE,SAAS,CAAC,SAAS;QAC9B,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,EAAE,EAAE,SAAS,CAAC,EAAE;KACjB,CAAA;AACH,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { BlockZones, ContentBlock, ContentValues } from './types.js';
2
+ /**
3
+ * Field-by-field and block-by-block. Never a diff of a serialisation.
4
+ *
5
+ * The L1 spec is blunt about why: a diff computed on the JSON text of an entry
6
+ * is unreadable, and unreadable is the same as absent — nobody approves a change
7
+ * they cannot see. An editor wants "the subtitle changed" and "the third block
8
+ * moved to second", not a wall of braces.
9
+ */
10
+ export type ChangeKind = 'added' | 'removed' | 'changed';
11
+ export interface FieldChange {
12
+ readonly field: string;
13
+ readonly change: ChangeKind;
14
+ readonly before: unknown;
15
+ readonly after: unknown;
16
+ }
17
+ export interface BlockChange {
18
+ /** The block zone this block belongs to. */
19
+ readonly zone: string;
20
+ /** The stable `_key` of the block; how a block is followed across versions. */
21
+ readonly key: string;
22
+ readonly type: string;
23
+ readonly change: ChangeKind | 'moved';
24
+ readonly fromIndex: number | null;
25
+ readonly toIndex: number | null;
26
+ /** Populated for `changed`: what changed inside the block. */
27
+ readonly fields: readonly FieldChange[];
28
+ }
29
+ export interface ContentDiff {
30
+ readonly fields: readonly FieldChange[];
31
+ readonly blocks: readonly BlockChange[];
32
+ readonly changed: boolean;
33
+ }
34
+ /**
35
+ * Structural equality, without a dependency (rule R9).
36
+ *
37
+ * Values here are JSON-shaped: what a field holds after decoding. Key order is
38
+ * not significant, so `{a,b}` and `{b,a}` compare equal — a diff that reported
39
+ * a change because the driver returned keys in another order would be noise.
40
+ */
41
+ export declare function deepEqual(left: unknown, right: unknown): boolean;
42
+ export declare function diffValues(before: ContentValues, after: ContentValues): FieldChange[];
43
+ /**
44
+ * Blocks are matched by `_key`, never by position.
45
+ *
46
+ * Matching by position would report an insertion at the top as "every block
47
+ * changed". Matching by key reports one addition and a set of moves, which is
48
+ * what actually happened.
49
+ */
50
+ export declare function diffBlocks(zone: string, before: readonly ContentBlock[], after: readonly ContentBlock[]): BlockChange[];
51
+ export declare function diffBlockZones(before: BlockZones, after: BlockZones): BlockChange[];
52
+ export declare function diffContent(before: {
53
+ readonly values: ContentValues;
54
+ readonly blocks: BlockZones;
55
+ }, after: {
56
+ readonly values: ContentValues;
57
+ readonly blocks: BlockZones;
58
+ }): ContentDiff;
59
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/store/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEzE;;;;;;;GAOG;AAEH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;AAExD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,+EAA+E;IAC/E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAA;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;CACxC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAoBhE;AAOD,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,GAAG,WAAW,EAAE,CAcrF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,KAAK,EAAE,SAAS,YAAY,EAAE,GAC7B,WAAW,EAAE,CAgEf;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,WAAW,EAAE,CAGnF;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,EACvE,KAAK,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GACrE,WAAW,CAIb"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Structural equality, without a dependency (rule R9).
3
+ *
4
+ * Values here are JSON-shaped: what a field holds after decoding. Key order is
5
+ * not significant, so `{a,b}` and `{b,a}` compare equal — a diff that reported
6
+ * a change because the driver returned keys in another order would be noise.
7
+ */
8
+ export function deepEqual(left, right) {
9
+ if (Object.is(left, right))
10
+ return true;
11
+ if (typeof left !== typeof right)
12
+ return false;
13
+ if (left === null || right === null)
14
+ return false;
15
+ if (typeof left !== 'object' || typeof right !== 'object')
16
+ return false;
17
+ if (Array.isArray(left) || Array.isArray(right)) {
18
+ if (!Array.isArray(left) || !Array.isArray(right))
19
+ return false;
20
+ if (left.length !== right.length)
21
+ return false;
22
+ return left.every((item, index) => deepEqual(item, right[index]));
23
+ }
24
+ const leftRecord = left;
25
+ const rightRecord = right;
26
+ const keys = new Set([...Object.keys(leftRecord), ...Object.keys(rightRecord)]);
27
+ for (const key of keys) {
28
+ if (!deepEqual(leftRecord[key], rightRecord[key]))
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ /** Undefined and null both mean "no value"; only one of them is ever stored. */
34
+ function isAbsent(value) {
35
+ return value === undefined || value === null;
36
+ }
37
+ export function diffValues(before, after) {
38
+ const fields = [...new Set([...Object.keys(before), ...Object.keys(after)])].sort();
39
+ const changes = [];
40
+ for (const field of fields) {
41
+ const from = before[field];
42
+ const to = after[field];
43
+ if (deepEqual(from, to))
44
+ continue;
45
+ const change = isAbsent(from) ? 'added' : isAbsent(to) ? 'removed' : 'changed';
46
+ changes.push({ field, change, before: from ?? null, after: to ?? null });
47
+ }
48
+ return changes;
49
+ }
50
+ /**
51
+ * Blocks are matched by `_key`, never by position.
52
+ *
53
+ * Matching by position would report an insertion at the top as "every block
54
+ * changed". Matching by key reports one addition and a set of moves, which is
55
+ * what actually happened.
56
+ */
57
+ export function diffBlocks(zone, before, after) {
58
+ const beforeIndex = new Map(before.map((block, index) => [block.key, { block, index }]));
59
+ const afterIndex = new Map(after.map((block, index) => [block.key, { block, index }]));
60
+ const changes = [];
61
+ for (const [key, { block, index }] of beforeIndex) {
62
+ if (afterIndex.has(key))
63
+ continue;
64
+ changes.push({
65
+ zone,
66
+ key,
67
+ type: block.type,
68
+ change: 'removed',
69
+ fromIndex: index,
70
+ toIndex: null,
71
+ fields: [],
72
+ });
73
+ }
74
+ for (const [key, { block, index }] of afterIndex) {
75
+ const previous = beforeIndex.get(key);
76
+ if (previous === undefined) {
77
+ changes.push({
78
+ zone,
79
+ key,
80
+ type: block.type,
81
+ change: 'added',
82
+ fromIndex: null,
83
+ toIndex: index,
84
+ fields: [],
85
+ });
86
+ continue;
87
+ }
88
+ const fields = diffValues(previous.block.data, block.data);
89
+ const retyped = previous.block.type !== block.type;
90
+ if (fields.length > 0 || retyped) {
91
+ changes.push({
92
+ zone,
93
+ key,
94
+ type: block.type,
95
+ change: 'changed',
96
+ fromIndex: previous.index,
97
+ toIndex: index,
98
+ fields,
99
+ });
100
+ }
101
+ else if (previous.index !== index) {
102
+ changes.push({
103
+ zone,
104
+ key,
105
+ type: block.type,
106
+ change: 'moved',
107
+ fromIndex: previous.index,
108
+ toIndex: index,
109
+ fields: [],
110
+ });
111
+ }
112
+ }
113
+ return changes.sort((left, right) => (left.toIndex ?? left.fromIndex ?? 0) - (right.toIndex ?? right.fromIndex ?? 0));
114
+ }
115
+ export function diffBlockZones(before, after) {
116
+ const zones = [...new Set([...Object.keys(before), ...Object.keys(after)])].sort();
117
+ return zones.flatMap((zone) => diffBlocks(zone, before[zone] ?? [], after[zone] ?? []));
118
+ }
119
+ export function diffContent(before, after) {
120
+ const fields = diffValues(before.values, after.values);
121
+ const blocks = diffBlockZones(before.blocks, after.blocks);
122
+ return { fields, blocks, changed: fields.length > 0 || blocks.length > 0 };
123
+ }
124
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/store/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { type SqlFragment } from '@cogenta/core';
2
+ /**
3
+ * Composition helpers over the `sql` tag of `@cogenta/core`.
4
+ *
5
+ * Everything dynamic in this package — a column list, a set clause, an `in` list
6
+ * — is built from bound fragments here. No call site ever writes a placeholder:
7
+ * Postgres wants `$1` and the others want `?`, and a hand-written one is both a
8
+ * dialect leak and the shortest path to an injection.
9
+ */
10
+ export declare function joinFragments(items: readonly SqlFragment[], separator: string): SqlFragment;
11
+ /** `(a, b, c)` from bound values, for an `in` predicate or an `insert`. */
12
+ export declare function valueList(values: readonly unknown[]): SqlFragment;
13
+ //# sourceMappingURL=fragments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragments.d.ts","sourceRoot":"","sources":["../../src/store/fragments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,WAAW,EAAkB,MAAM,eAAe,CAAA;AAE9E;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAW3F;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,WAAW,CAKjE"}
@@ -0,0 +1,25 @@
1
+ import { CogentaError, sql, unsafeRaw } from '@cogenta/core';
2
+ /**
3
+ * Composition helpers over the `sql` tag of `@cogenta/core`.
4
+ *
5
+ * Everything dynamic in this package — a column list, a set clause, an `in` list
6
+ * — is built from bound fragments here. No call site ever writes a placeholder:
7
+ * Postgres wants `$1` and the others want `?`, and a hand-written one is both a
8
+ * dialect leak and the shortest path to an injection.
9
+ */
10
+ export function joinFragments(items, separator) {
11
+ if (items.length === 0) {
12
+ throw new CogentaError({
13
+ code: 'INTERNAL',
14
+ message: 'Tried to build an empty SQL list.',
15
+ hint: 'This is a bug: the caller should have skipped the statement entirely.',
16
+ });
17
+ }
18
+ const glue = unsafeRaw(separator);
19
+ return items.reduce((left, right) => sql `${left}${glue}${right}`);
20
+ }
21
+ /** `(a, b, c)` from bound values, for an `in` predicate or an `insert`. */
22
+ export function valueList(values) {
23
+ return joinFragments(values.map((value) => sql `${value}`), ', ');
24
+ }
25
+ //# sourceMappingURL=fragments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragments.js","sourceRoot":"","sources":["../../src/store/fragments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAoB,GAAG,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9E;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAA6B,EAAE,SAAiB;IAC5E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,mCAAmC;YAC5C,IAAI,EAAE,uEAAuE;SAC9E,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,CAAA;IACjC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAA;AACnE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,SAAS,CAAC,MAA0B;IAClD,OAAO,aAAa,CAClB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,KAAK,EAAE,CAAC,EACpC,IAAI,CACL,CAAA;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The content persistence layer: CRUD, drafts, versions, diff and i18n.
3
+ *
4
+ * Everything here is portable across Postgres, MySQL, MariaDB and SQLite by
5
+ * going through `DatabaseHandle` from `@cogenta/core`. No dialect difference
6
+ * leaks past this folder, and no SQL is written outside it.
7
+ */
8
+ export { booleanColumn, columnTypeFor, integerColumn, isColumnless, jsonColumn, onDeleteClause, textColumn, timestampColumn, uuidColumn, } from './columns.js';
9
+ export type { Cursor } from './cursor.js';
10
+ export { decodeCursor, encodeCursor } from './cursor.js';
11
+ export type { BlockChange, ChangeKind, ContentDiff, FieldChange } from './diff.js';
12
+ export { deepEqual, diffBlocks, diffBlockZones, diffContent, diffValues } from './diff.js';
13
+ export { blocksTable, columnFor, entriesTable, isSystemColumn, relationTable, SYSTEM_COLUMNS, toSnakeCase, versionsTable, } from './naming.js';
14
+ export { withReadOnlyStore } from './read-only.js';
15
+ export type { ContentStore, ContentStoreOptions } from './store.js';
16
+ export { createContentStore } from './store.js';
17
+ export type { RelationTarget } from './tables.js';
18
+ export { assertUsableFields, createSchemaTables, dropSchemaTables, orderByDependency, relationsOf, } from './tables.js';
19
+ export type { BlockZones, ContentBlock, ContentEntry, ContentValues, CreateInput, EntryState, ListOptions, LocaleFallback, LocaleResolution, Page, PublishInput, ResolveLocaleOptions, SortField, SortOrder, UpdateInput, VersionSummary, } from './types.js';
20
+ export type { NormalisedValues, NormaliseOptions } from './values.js';
21
+ export { decodeFieldValue, encodeFieldValue, normaliseBlocks, normaliseValues } from './values.js';
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,GACX,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAClF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC1F,OAAO,EACL,WAAW,EACX,SAAS,EACT,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAClD,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,IAAI,EACJ,YAAY,EACZ,oBAAoB,EACpB,SAAS,EACT,SAAS,EACT,WAAW,EACX,cAAc,GACf,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The content persistence layer: CRUD, drafts, versions, diff and i18n.
3
+ *
4
+ * Everything here is portable across Postgres, MySQL, MariaDB and SQLite by
5
+ * going through `DatabaseHandle` from `@cogenta/core`. No dialect difference
6
+ * leaks past this folder, and no SQL is written outside it.
7
+ */
8
+ export { booleanColumn, columnTypeFor, integerColumn, isColumnless, jsonColumn, onDeleteClause, textColumn, timestampColumn, uuidColumn, } from './columns.js';
9
+ export { decodeCursor, encodeCursor } from './cursor.js';
10
+ export { deepEqual, diffBlocks, diffBlockZones, diffContent, diffValues } from './diff.js';
11
+ export { blocksTable, columnFor, entriesTable, isSystemColumn, relationTable, SYSTEM_COLUMNS, toSnakeCase, versionsTable, } from './naming.js';
12
+ export { withReadOnlyStore } from './read-only.js';
13
+ export { createContentStore } from './store.js';
14
+ export { assertUsableFields, createSchemaTables, dropSchemaTables, orderByDependency, relationsOf, } from './tables.js';
15
+ export { decodeFieldValue, encodeFieldValue, normaliseBlocks, normaliseValues } from './values.js';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,GACX,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAExD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC1F,OAAO,EACL,WAAW,EACX,SAAS,EACT,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,GACd,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAA;AAoBpB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA"}