@dalgoridim/headless-cms 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 (87) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +178 -0
  3. package/dist/adapters/firestore/index.cjs +152 -0
  4. package/dist/adapters/firestore/index.cjs.map +1 -0
  5. package/dist/adapters/firestore/index.d.cts +39 -0
  6. package/dist/adapters/firestore/index.d.ts +39 -0
  7. package/dist/adapters/firestore/index.js +120 -0
  8. package/dist/adapters/firestore/index.js.map +1 -0
  9. package/dist/adapters/postgres/index.cjs +299 -0
  10. package/dist/adapters/postgres/index.cjs.map +1 -0
  11. package/dist/adapters/postgres/index.d.cts +59 -0
  12. package/dist/adapters/postgres/index.d.ts +59 -0
  13. package/dist/adapters/postgres/index.js +277 -0
  14. package/dist/adapters/postgres/index.js.map +1 -0
  15. package/dist/auth/firebase/client/index.cjs +153 -0
  16. package/dist/auth/firebase/client/index.cjs.map +1 -0
  17. package/dist/auth/firebase/client/index.d.cts +29 -0
  18. package/dist/auth/firebase/client/index.d.ts +29 -0
  19. package/dist/auth/firebase/client/index.js +138 -0
  20. package/dist/auth/firebase/client/index.js.map +1 -0
  21. package/dist/auth/firebase/index.cjs +81 -0
  22. package/dist/auth/firebase/index.cjs.map +1 -0
  23. package/dist/auth/firebase/index.d.cts +23 -0
  24. package/dist/auth/firebase/index.d.ts +23 -0
  25. package/dist/auth/firebase/index.js +46 -0
  26. package/dist/auth/firebase/index.js.map +1 -0
  27. package/dist/auth/nextauth/index.cjs +51 -0
  28. package/dist/auth/nextauth/index.cjs.map +1 -0
  29. package/dist/auth/nextauth/index.d.cts +30 -0
  30. package/dist/auth/nextauth/index.d.ts +30 -0
  31. package/dist/auth/nextauth/index.js +25 -0
  32. package/dist/auth/nextauth/index.js.map +1 -0
  33. package/dist/client/index.cjs +1018 -0
  34. package/dist/client/index.cjs.map +1 -0
  35. package/dist/client/index.d.cts +96 -0
  36. package/dist/client/index.d.ts +96 -0
  37. package/dist/client/index.js +994 -0
  38. package/dist/client/index.js.map +1 -0
  39. package/dist/index.cjs +19 -0
  40. package/dist/index.cjs.map +1 -0
  41. package/dist/index.d.cts +122 -0
  42. package/dist/index.d.ts +122 -0
  43. package/dist/index.js +1 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/server/index.cjs +128 -0
  46. package/dist/server/index.cjs.map +1 -0
  47. package/dist/server/index.d.cts +52 -0
  48. package/dist/server/index.d.ts +52 -0
  49. package/dist/server/index.js +99 -0
  50. package/dist/server/index.js.map +1 -0
  51. package/dist/storage/cloudinary/index.cjs +55 -0
  52. package/dist/storage/cloudinary/index.cjs.map +1 -0
  53. package/dist/storage/cloudinary/index.d.cts +17 -0
  54. package/dist/storage/cloudinary/index.d.ts +17 -0
  55. package/dist/storage/cloudinary/index.js +30 -0
  56. package/dist/storage/cloudinary/index.js.map +1 -0
  57. package/dist/storage/cloudinary/server.cjs +56 -0
  58. package/dist/storage/cloudinary/server.cjs.map +1 -0
  59. package/dist/storage/cloudinary/server.d.cts +16 -0
  60. package/dist/storage/cloudinary/server.d.ts +16 -0
  61. package/dist/storage/cloudinary/server.js +31 -0
  62. package/dist/storage/cloudinary/server.js.map +1 -0
  63. package/dist/storage/local/index.cjs +44 -0
  64. package/dist/storage/local/index.cjs.map +1 -0
  65. package/dist/storage/local/index.d.cts +15 -0
  66. package/dist/storage/local/index.d.ts +15 -0
  67. package/dist/storage/local/index.js +19 -0
  68. package/dist/storage/local/index.js.map +1 -0
  69. package/dist/storage/local/server.cjs +61 -0
  70. package/dist/storage/local/server.cjs.map +1 -0
  71. package/dist/storage/local/server.d.cts +16 -0
  72. package/dist/storage/local/server.d.ts +16 -0
  73. package/dist/storage/local/server.js +26 -0
  74. package/dist/storage/local/server.js.map +1 -0
  75. package/dist/storage/s3/index.cjs +52 -0
  76. package/dist/storage/s3/index.cjs.map +1 -0
  77. package/dist/storage/s3/index.d.cts +14 -0
  78. package/dist/storage/s3/index.d.ts +14 -0
  79. package/dist/storage/s3/index.js +27 -0
  80. package/dist/storage/s3/index.js.map +1 -0
  81. package/dist/storage/s3/server.cjs +61 -0
  82. package/dist/storage/s3/server.cjs.map +1 -0
  83. package/dist/storage/s3/server.d.cts +19 -0
  84. package/dist/storage/s3/server.d.ts +19 -0
  85. package/dist/storage/s3/server.js +36 -0
  86. package/dist/storage/s3/server.js.map +1 -0
  87. package/package.json +165 -0
@@ -0,0 +1,299 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+ var __export = (target, all) => {
33
+ for (var name in all)
34
+ __defProp(target, name, { get: all[name], enumerable: true });
35
+ };
36
+ var __copyProps = (to, from, except, desc) => {
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (let key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(to, key) && key !== except)
40
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
41
+ }
42
+ return to;
43
+ };
44
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
45
+
46
+ // src/adapters/postgres/index.ts
47
+ var postgres_exports = {};
48
+ __export(postgres_exports, {
49
+ PostgresDataAdapter: () => PostgresDataAdapter
50
+ });
51
+ module.exports = __toCommonJS(postgres_exports);
52
+ var import_pg = require("pg");
53
+ var import_node_crypto = require("crypto");
54
+ var OP_MAP = {
55
+ eq: "=",
56
+ lt: "<",
57
+ lte: "<=",
58
+ gt: ">",
59
+ gte: ">=",
60
+ in: "= ANY"
61
+ };
62
+ var SQL_TYPE = {
63
+ text: "text",
64
+ int: "integer",
65
+ float: "double precision",
66
+ bool: "boolean",
67
+ date: "date",
68
+ timestamptz: "timestamptz",
69
+ jsonb: "jsonb"
70
+ };
71
+ var PostgresDataAdapter = class {
72
+ constructor(config = {}) {
73
+ var _a, _b;
74
+ if (config.pool) {
75
+ this.pool = config.pool;
76
+ } else if (config.connectionString) {
77
+ this.pool = new import_pg.Pool({ connectionString: config.connectionString });
78
+ } else {
79
+ throw new Error(
80
+ "PostgresDataAdapter requires either `pool` or `connectionString`."
81
+ );
82
+ }
83
+ this.collections = (_a = config.collections) != null ? _a : {};
84
+ this.documentsTable = (_b = config.documentsTable) != null ? _b : "documents";
85
+ }
86
+ cfg(collection) {
87
+ return this.collections[collection];
88
+ }
89
+ /** Create the documents table and every registered typed table if missing. */
90
+ async migrate() {
91
+ await this.pool.query(`
92
+ CREATE TABLE IF NOT EXISTS ${ident(this.documentsTable)} (
93
+ id text NOT NULL,
94
+ collection text NOT NULL,
95
+ data jsonb NOT NULL,
96
+ created_at timestamptz DEFAULT now(),
97
+ updated_at timestamptz DEFAULT now(),
98
+ PRIMARY KEY (collection, id)
99
+ );
100
+ `);
101
+ await this.pool.query(
102
+ `CREATE INDEX IF NOT EXISTS ${ident(this.documentsTable + "_collection_created_idx")}
103
+ ON ${ident(this.documentsTable)} (collection, created_at DESC);`
104
+ );
105
+ for (const cfg of Object.values(this.collections)) {
106
+ const cols = Object.entries(cfg.columns).map(([name, type]) => `${ident(name)} ${SQL_TYPE[type]}`).join(",\n ");
107
+ await this.pool.query(`
108
+ CREATE TABLE IF NOT EXISTS ${ident(cfg.table)} (
109
+ id text PRIMARY KEY,
110
+ ${cols ? cols + "," : ""}
111
+ extra jsonb NOT NULL DEFAULT '{}'::jsonb,
112
+ created_at timestamptz DEFAULT now(),
113
+ updated_at timestamptz DEFAULT now()
114
+ );
115
+ `);
116
+ }
117
+ }
118
+ // --- row <-> section mapping ------------------------------------------------
119
+ /** Split a section into typed columns + leftover `extra`. */
120
+ toTypedRow(cfg, data) {
121
+ const known = {};
122
+ const extra = {};
123
+ for (const [key, value] of Object.entries(data)) {
124
+ if (key === "id" || key === "collection") continue;
125
+ if (key in cfg.columns) known[key] = value;
126
+ else extra[key] = value;
127
+ }
128
+ return { known, extra };
129
+ }
130
+ fromTypedRow(collection, cfg, row) {
131
+ const _a = row, { id, extra, created_at, updated_at } = _a, rest = __objRest(_a, ["id", "extra", "created_at", "updated_at"]);
132
+ void created_at;
133
+ void updated_at;
134
+ return __spreadValues(__spreadValues({
135
+ id,
136
+ collection
137
+ }, rest), extra != null ? extra : {});
138
+ }
139
+ // --- reads -----------------------------------------------------------------
140
+ async fetchById(collection, id) {
141
+ const cfg = this.cfg(collection);
142
+ if (cfg) {
143
+ const { rows: rows2 } = await this.pool.query(
144
+ `SELECT * FROM ${ident(cfg.table)} WHERE id = $1`,
145
+ [id]
146
+ );
147
+ if (!rows2[0]) return null;
148
+ return this.fromTypedRow(collection, cfg, rows2[0]);
149
+ }
150
+ const { rows } = await this.pool.query(
151
+ `SELECT id, data FROM ${ident(this.documentsTable)} WHERE id = $1 AND collection = $2`,
152
+ [id, collection]
153
+ );
154
+ if (!rows[0]) return null;
155
+ return __spreadValues({ id: rows[0].id, collection }, rows[0].data);
156
+ }
157
+ async fetchCollection(collection, q) {
158
+ var _a, _b;
159
+ const cfg = this.cfg(collection);
160
+ const params = [];
161
+ const where = [];
162
+ const colExpr = (field) => {
163
+ if (cfg) {
164
+ return field in cfg.columns ? ident(field) : `extra->>${literal(field)}`;
165
+ }
166
+ return `data->>${literal(field)}`;
167
+ };
168
+ if (!cfg) {
169
+ params.push(collection);
170
+ where.push(`collection = $${params.length}`);
171
+ }
172
+ for (const f of (_a = q == null ? void 0 : q.filters) != null ? _a : []) {
173
+ params.push(f.value);
174
+ if (f.op === "in") {
175
+ where.push(`${colExpr(f.field)} = ANY($${params.length})`);
176
+ } else {
177
+ where.push(`${colExpr(f.field)} ${OP_MAP[f.op]} $${params.length}`);
178
+ }
179
+ }
180
+ const orderBy = ((_b = q == null ? void 0 : q.orderBy) != null ? _b : []).map((o) => `${colExpr(o.field)} ${o.direction === "asc" ? "ASC" : "DESC"}`).join(", ") || "created_at DESC";
181
+ const limit = (q == null ? void 0 : q.limit) != null ? ` LIMIT ${Number(q.limit)}` : "";
182
+ const whereSql = where.length ? ` WHERE ${where.join(" AND ")}` : "";
183
+ const table = cfg ? cfg.table : this.documentsTable;
184
+ const select = cfg ? "*" : "id, data";
185
+ const { rows } = await this.pool.query(
186
+ `SELECT ${select} FROM ${ident(table)}${whereSql} ORDER BY ${orderBy}${limit}`,
187
+ params
188
+ );
189
+ if (cfg) {
190
+ return rows.map((r) => this.fromTypedRow(collection, cfg, r));
191
+ }
192
+ return rows.map((r) => __spreadValues({ id: r.id, collection }, r.data));
193
+ }
194
+ // --- writes ----------------------------------------------------------------
195
+ async create(collection, data) {
196
+ return this.createWithId(collection, (0, import_node_crypto.randomUUID)(), data);
197
+ }
198
+ async createWithId(collection, id, data) {
199
+ const cfg = this.cfg(collection);
200
+ const record = data;
201
+ if (cfg) {
202
+ const { known, extra } = this.toTypedRow(cfg, record);
203
+ const cols = ["id", ...Object.keys(known), "extra"];
204
+ const vals = [id, ...Object.values(known), JSON.stringify(extra)];
205
+ const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
206
+ await this.pool.query(
207
+ `INSERT INTO ${ident(cfg.table)} (${cols.map(ident).join(", ")})
208
+ VALUES (${placeholders})`,
209
+ vals
210
+ );
211
+ } else {
212
+ await this.pool.query(
213
+ `INSERT INTO ${ident(this.documentsTable)} (id, collection, data)
214
+ VALUES ($1, $2, $3)`,
215
+ [id, collection, JSON.stringify(record)]
216
+ );
217
+ }
218
+ return __spreadValues({ id }, data);
219
+ }
220
+ async update(collection, id, data) {
221
+ await this.write(collection, id, data, "update");
222
+ }
223
+ async upsert(collection, id, data) {
224
+ await this.write(collection, id, data, "upsert");
225
+ }
226
+ async write(collection, id, data, mode) {
227
+ const cfg = this.cfg(collection);
228
+ if (cfg) {
229
+ const { known, extra } = this.toTypedRow(cfg, data);
230
+ const setCols = Object.keys(known);
231
+ const setExpr = setCols.map((c, i) => `${ident(c)} = $${i + 2}`).join(", ");
232
+ const extraIdx = setCols.length + 2;
233
+ const sets = [
234
+ setExpr,
235
+ // Table-qualified: in ON CONFLICT DO UPDATE both the target table and the
236
+ // `excluded` pseudo-relation expose `extra`, so a bare ref is ambiguous.
237
+ `extra = ${ident(cfg.table)}.${ident("extra")} || $${extraIdx}::jsonb`,
238
+ `updated_at = now()`
239
+ ].filter(Boolean).join(", ");
240
+ const params = [id, ...Object.values(known), JSON.stringify(extra)];
241
+ if (mode === "upsert") {
242
+ const insertCols = ["id", ...setCols, "extra"];
243
+ const insertVals = params;
244
+ const placeholders = insertCols.map((_, i) => `$${i + 1}`).join(", ");
245
+ await this.pool.query(
246
+ `INSERT INTO ${ident(cfg.table)} (${insertCols.map(ident).join(", ")})
247
+ VALUES (${placeholders})
248
+ ON CONFLICT (id) DO UPDATE SET ${sets}`,
249
+ insertVals
250
+ );
251
+ } else {
252
+ await this.pool.query(
253
+ `UPDATE ${ident(cfg.table)} SET ${sets} WHERE id = $1`,
254
+ params
255
+ );
256
+ }
257
+ return;
258
+ }
259
+ if (mode === "upsert") {
260
+ await this.pool.query(
261
+ `INSERT INTO ${ident(this.documentsTable)} (id, collection, data)
262
+ VALUES ($1, $2, $3)
263
+ ON CONFLICT (collection, id) DO UPDATE
264
+ SET data = ${ident(this.documentsTable)}.data || $3::jsonb,
265
+ updated_at = now()`,
266
+ [id, collection, JSON.stringify(data)]
267
+ );
268
+ } else {
269
+ await this.pool.query(
270
+ `UPDATE ${ident(this.documentsTable)}
271
+ SET data = data || $2::jsonb, updated_at = now()
272
+ WHERE id = $1 AND collection = $3`,
273
+ [id, JSON.stringify(data), collection]
274
+ );
275
+ }
276
+ }
277
+ async delete(collection, id) {
278
+ const cfg = this.cfg(collection);
279
+ if (cfg) {
280
+ await this.pool.query(`DELETE FROM ${ident(cfg.table)} WHERE id = $1`, [id]);
281
+ } else {
282
+ await this.pool.query(
283
+ `DELETE FROM ${ident(this.documentsTable)} WHERE id = $1 AND collection = $2`,
284
+ [id, collection]
285
+ );
286
+ }
287
+ }
288
+ };
289
+ function ident(name) {
290
+ return `"${name.replace(/"/g, '""')}"`;
291
+ }
292
+ function literal(value) {
293
+ return `'${value.replace(/'/g, "''")}'`;
294
+ }
295
+ // Annotate the CommonJS export names for ESM import in node:
296
+ 0 && (module.exports = {
297
+ PostgresDataAdapter
298
+ });
299
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/adapters/postgres/index.ts"],"sourcesContent":["import { Pool } from \"pg\";\nimport { randomUUID } from \"node:crypto\";\nimport type { DataAdapter, Query, QueryFilterOp } from \"../../types\";\n\n/** Column type hints for a registered (typed) collection. */\nexport type ColumnType = \"text\" | \"int\" | \"float\" | \"bool\" | \"date\" | \"timestamptz\" | \"jsonb\";\n\nexport interface CollectionConfig {\n /** Physical table name. */\n table: string;\n /** Map of section field → SQL column type. Unlisted fields fall into `extra`. */\n columns: Record<string, ColumnType>;\n}\n\nexport interface PostgresAdapterConfig {\n /** Provide a connection string… */\n connectionString?: string;\n /** …or an existing pg Pool. */\n pool?: Pool;\n /**\n * Registry of typed collections. Anything not registered here is stored in\n * the shared JSONB `documents` table.\n */\n collections?: Record<string, CollectionConfig>;\n /** Name of the fallback JSONB table. Default `documents`. */\n documentsTable?: string;\n}\n\nconst OP_MAP: Record<QueryFilterOp, string> = {\n eq: \"=\",\n lt: \"<\",\n lte: \"<=\",\n gt: \">\",\n gte: \">=\",\n in: \"= ANY\",\n};\n\nconst SQL_TYPE: Record<ColumnType, string> = {\n text: \"text\",\n int: \"integer\",\n float: \"double precision\",\n bool: \"boolean\",\n date: \"date\",\n timestamptz: \"timestamptz\",\n jsonb: \"jsonb\",\n};\n\n/**\n * Hybrid Postgres adapter. Unregistered collections live in a shared JSONB\n * `documents` table; registered collections map flat section fields onto typed\n * columns, with a JSONB `extra` column so unmapped fields are never dropped.\n */\nexport class PostgresDataAdapter implements DataAdapter {\n private readonly pool: Pool;\n private readonly collections: Record<string, CollectionConfig>;\n private readonly documentsTable: string;\n\n constructor(config: PostgresAdapterConfig = {}) {\n if (config.pool) {\n this.pool = config.pool;\n } else if (config.connectionString) {\n this.pool = new Pool({ connectionString: config.connectionString });\n } else {\n throw new Error(\n \"PostgresDataAdapter requires either `pool` or `connectionString`.\",\n );\n }\n this.collections = config.collections ?? {};\n this.documentsTable = config.documentsTable ?? \"documents\";\n }\n\n private cfg(collection: string): CollectionConfig | undefined {\n return this.collections[collection];\n }\n\n /** Create the documents table and every registered typed table if missing. */\n async migrate(): Promise<void> {\n await this.pool.query(`\n CREATE TABLE IF NOT EXISTS ${ident(this.documentsTable)} (\n id text NOT NULL,\n collection text NOT NULL,\n data jsonb NOT NULL,\n created_at timestamptz DEFAULT now(),\n updated_at timestamptz DEFAULT now(),\n PRIMARY KEY (collection, id)\n );\n `);\n await this.pool.query(\n `CREATE INDEX IF NOT EXISTS ${ident(this.documentsTable + \"_collection_created_idx\")}\n ON ${ident(this.documentsTable)} (collection, created_at DESC);`,\n );\n\n for (const cfg of Object.values(this.collections)) {\n const cols = Object.entries(cfg.columns)\n .map(([name, type]) => `${ident(name)} ${SQL_TYPE[type]}`)\n .join(\",\\n \");\n await this.pool.query(`\n CREATE TABLE IF NOT EXISTS ${ident(cfg.table)} (\n id text PRIMARY KEY,\n ${cols ? cols + \",\" : \"\"}\n extra jsonb NOT NULL DEFAULT '{}'::jsonb,\n created_at timestamptz DEFAULT now(),\n updated_at timestamptz DEFAULT now()\n );\n `);\n }\n }\n\n // --- row <-> section mapping ------------------------------------------------\n\n /** Split a section into typed columns + leftover `extra`. */\n private toTypedRow(cfg: CollectionConfig, data: Record<string, unknown>) {\n const known: Record<string, unknown> = {};\n const extra: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key === \"id\" || key === \"collection\") continue;\n if (key in cfg.columns) known[key] = value;\n else extra[key] = value;\n }\n return { known, extra };\n }\n\n private fromTypedRow(\n collection: string,\n cfg: CollectionConfig,\n row: Record<string, unknown>,\n ) {\n const { id, extra, created_at, updated_at, ...rest } = row;\n void created_at;\n void updated_at;\n return {\n id: id as string,\n collection,\n ...(rest as Record<string, unknown>),\n ...((extra as Record<string, unknown>) ?? {}),\n };\n }\n\n // --- reads -----------------------------------------------------------------\n\n async fetchById<T = Record<string, unknown>>(\n collection: string,\n id: string,\n ): Promise<(T & { id: string }) | null> {\n const cfg = this.cfg(collection);\n if (cfg) {\n const { rows } = await this.pool.query(\n `SELECT * FROM ${ident(cfg.table)} WHERE id = $1`,\n [id],\n );\n if (!rows[0]) return null;\n return this.fromTypedRow(collection, cfg, rows[0]) as unknown as T & {\n id: string;\n };\n }\n const { rows } = await this.pool.query(\n `SELECT id, data FROM ${ident(this.documentsTable)} WHERE id = $1 AND collection = $2`,\n [id, collection],\n );\n if (!rows[0]) return null;\n return { id: rows[0].id, collection, ...rows[0].data } as unknown as T & {\n id: string;\n };\n }\n\n async fetchCollection<T = Record<string, unknown>>(\n collection: string,\n q?: Query,\n ): Promise<(T & { id: string })[]> {\n const cfg = this.cfg(collection);\n const params: unknown[] = [];\n const where: string[] = [];\n\n const colExpr = (field: string): string => {\n if (cfg) {\n return field in cfg.columns ? ident(field) : `extra->>${literal(field)}`;\n }\n return `data->>${literal(field)}`;\n };\n\n if (!cfg) {\n params.push(collection);\n where.push(`collection = $${params.length}`);\n }\n\n for (const f of q?.filters ?? []) {\n params.push(f.value);\n if (f.op === \"in\") {\n where.push(`${colExpr(f.field)} = ANY($${params.length})`);\n } else {\n where.push(`${colExpr(f.field)} ${OP_MAP[f.op]} $${params.length}`);\n }\n }\n\n const orderBy =\n (q?.orderBy ?? []).map((o) => `${colExpr(o.field)} ${o.direction === \"asc\" ? \"ASC\" : \"DESC\"}`).join(\", \") ||\n \"created_at DESC\";\n\n const limit = q?.limit != null ? ` LIMIT ${Number(q.limit)}` : \"\";\n const whereSql = where.length ? ` WHERE ${where.join(\" AND \")}` : \"\";\n const table = cfg ? cfg.table : this.documentsTable;\n const select = cfg ? \"*\" : \"id, data\";\n\n const { rows } = await this.pool.query(\n `SELECT ${select} FROM ${ident(table)}${whereSql} ORDER BY ${orderBy}${limit}`,\n params,\n );\n\n if (cfg) {\n return rows.map((r) => this.fromTypedRow(collection, cfg, r)) as unknown as (T & {\n id: string;\n })[];\n }\n return rows.map((r) => ({ id: r.id, collection, ...r.data })) as unknown as (T & {\n id: string;\n })[];\n }\n\n // --- writes ----------------------------------------------------------------\n\n async create<T = Record<string, unknown>>(\n collection: string,\n data: T,\n ): Promise<T & { id: string }> {\n return this.createWithId(collection, randomUUID(), data);\n }\n\n async createWithId<T = Record<string, unknown>>(\n collection: string,\n id: string,\n data: T,\n ): Promise<T & { id: string }> {\n const cfg = this.cfg(collection);\n const record = data as Record<string, unknown>;\n\n if (cfg) {\n const { known, extra } = this.toTypedRow(cfg, record);\n const cols = [\"id\", ...Object.keys(known), \"extra\"];\n const vals = [id, ...Object.values(known), JSON.stringify(extra)];\n const placeholders = cols.map((_, i) => `$${i + 1}`).join(\", \");\n await this.pool.query(\n `INSERT INTO ${ident(cfg.table)} (${cols.map(ident).join(\", \")})\n VALUES (${placeholders})`,\n vals,\n );\n } else {\n await this.pool.query(\n `INSERT INTO ${ident(this.documentsTable)} (id, collection, data)\n VALUES ($1, $2, $3)`,\n [id, collection, JSON.stringify(record)],\n );\n }\n return { id, ...(data as T) };\n }\n\n async update<T = Record<string, unknown>>(\n collection: string,\n id: string,\n data: Partial<T>,\n ): Promise<void> {\n await this.write(collection, id, data, \"update\");\n }\n\n async upsert<T = Record<string, unknown>>(\n collection: string,\n id: string,\n data: Partial<T>,\n ): Promise<void> {\n await this.write(collection, id, data, \"upsert\");\n }\n\n private async write(\n collection: string,\n id: string,\n data: Record<string, unknown>,\n mode: \"update\" | \"upsert\",\n ): Promise<void> {\n const cfg = this.cfg(collection);\n\n if (cfg) {\n const { known, extra } = this.toTypedRow(cfg, data);\n const setCols = Object.keys(known);\n const setExpr = setCols\n .map((c, i) => `${ident(c)} = $${i + 2}`)\n .join(\", \");\n // Merge into extra so unmapped fields aren't dropped.\n const extraIdx = setCols.length + 2;\n const sets = [\n setExpr,\n // Table-qualified: in ON CONFLICT DO UPDATE both the target table and the\n // `excluded` pseudo-relation expose `extra`, so a bare ref is ambiguous.\n `extra = ${ident(cfg.table)}.${ident(\"extra\")} || $${extraIdx}::jsonb`,\n `updated_at = now()`,\n ]\n .filter(Boolean)\n .join(\", \");\n const params = [id, ...Object.values(known), JSON.stringify(extra)];\n\n if (mode === \"upsert\") {\n const insertCols = [\"id\", ...setCols, \"extra\"];\n const insertVals = params;\n const placeholders = insertCols.map((_, i) => `$${i + 1}`).join(\", \");\n await this.pool.query(\n `INSERT INTO ${ident(cfg.table)} (${insertCols.map(ident).join(\", \")})\n VALUES (${placeholders})\n ON CONFLICT (id) DO UPDATE SET ${sets}`,\n insertVals,\n );\n } else {\n await this.pool.query(\n `UPDATE ${ident(cfg.table)} SET ${sets} WHERE id = $1`,\n params,\n );\n }\n return;\n }\n\n // JSONB documents table: shallow-merge the patch into `data`.\n if (mode === \"upsert\") {\n await this.pool.query(\n `INSERT INTO ${ident(this.documentsTable)} (id, collection, data)\n VALUES ($1, $2, $3)\n ON CONFLICT (collection, id) DO UPDATE\n SET data = ${ident(this.documentsTable)}.data || $3::jsonb,\n updated_at = now()`,\n [id, collection, JSON.stringify(data)],\n );\n } else {\n await this.pool.query(\n `UPDATE ${ident(this.documentsTable)}\n SET data = data || $2::jsonb, updated_at = now()\n WHERE id = $1 AND collection = $3`,\n [id, JSON.stringify(data), collection],\n );\n }\n }\n\n async delete(collection: string, id: string): Promise<void> {\n const cfg = this.cfg(collection);\n if (cfg) {\n await this.pool.query(`DELETE FROM ${ident(cfg.table)} WHERE id = $1`, [id]);\n } else {\n await this.pool.query(\n `DELETE FROM ${ident(this.documentsTable)} WHERE id = $1 AND collection = $2`,\n [id, collection],\n );\n }\n }\n}\n\n/** Quote a SQL identifier safely. */\nfunction ident(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\n/** Quote a SQL string literal (used for static jsonb keys, never user values). */\nfunction literal(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n"],"mappings":"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","names":["rows"]}
@@ -0,0 +1,59 @@
1
+ import { Pool } from 'pg';
2
+ import { DataAdapter, Query } from '../../index.cjs';
3
+
4
+ /** Column type hints for a registered (typed) collection. */
5
+ type ColumnType = "text" | "int" | "float" | "bool" | "date" | "timestamptz" | "jsonb";
6
+ interface CollectionConfig {
7
+ /** Physical table name. */
8
+ table: string;
9
+ /** Map of section field → SQL column type. Unlisted fields fall into `extra`. */
10
+ columns: Record<string, ColumnType>;
11
+ }
12
+ interface PostgresAdapterConfig {
13
+ /** Provide a connection string… */
14
+ connectionString?: string;
15
+ /** …or an existing pg Pool. */
16
+ pool?: Pool;
17
+ /**
18
+ * Registry of typed collections. Anything not registered here is stored in
19
+ * the shared JSONB `documents` table.
20
+ */
21
+ collections?: Record<string, CollectionConfig>;
22
+ /** Name of the fallback JSONB table. Default `documents`. */
23
+ documentsTable?: string;
24
+ }
25
+ /**
26
+ * Hybrid Postgres adapter. Unregistered collections live in a shared JSONB
27
+ * `documents` table; registered collections map flat section fields onto typed
28
+ * columns, with a JSONB `extra` column so unmapped fields are never dropped.
29
+ */
30
+ declare class PostgresDataAdapter implements DataAdapter {
31
+ private readonly pool;
32
+ private readonly collections;
33
+ private readonly documentsTable;
34
+ constructor(config?: PostgresAdapterConfig);
35
+ private cfg;
36
+ /** Create the documents table and every registered typed table if missing. */
37
+ migrate(): Promise<void>;
38
+ /** Split a section into typed columns + leftover `extra`. */
39
+ private toTypedRow;
40
+ private fromTypedRow;
41
+ fetchById<T = Record<string, unknown>>(collection: string, id: string): Promise<(T & {
42
+ id: string;
43
+ }) | null>;
44
+ fetchCollection<T = Record<string, unknown>>(collection: string, q?: Query): Promise<(T & {
45
+ id: string;
46
+ })[]>;
47
+ create<T = Record<string, unknown>>(collection: string, data: T): Promise<T & {
48
+ id: string;
49
+ }>;
50
+ createWithId<T = Record<string, unknown>>(collection: string, id: string, data: T): Promise<T & {
51
+ id: string;
52
+ }>;
53
+ update<T = Record<string, unknown>>(collection: string, id: string, data: Partial<T>): Promise<void>;
54
+ upsert<T = Record<string, unknown>>(collection: string, id: string, data: Partial<T>): Promise<void>;
55
+ private write;
56
+ delete(collection: string, id: string): Promise<void>;
57
+ }
58
+
59
+ export { type CollectionConfig, type ColumnType, type PostgresAdapterConfig, PostgresDataAdapter };
@@ -0,0 +1,59 @@
1
+ import { Pool } from 'pg';
2
+ import { DataAdapter, Query } from '../../index.js';
3
+
4
+ /** Column type hints for a registered (typed) collection. */
5
+ type ColumnType = "text" | "int" | "float" | "bool" | "date" | "timestamptz" | "jsonb";
6
+ interface CollectionConfig {
7
+ /** Physical table name. */
8
+ table: string;
9
+ /** Map of section field → SQL column type. Unlisted fields fall into `extra`. */
10
+ columns: Record<string, ColumnType>;
11
+ }
12
+ interface PostgresAdapterConfig {
13
+ /** Provide a connection string… */
14
+ connectionString?: string;
15
+ /** …or an existing pg Pool. */
16
+ pool?: Pool;
17
+ /**
18
+ * Registry of typed collections. Anything not registered here is stored in
19
+ * the shared JSONB `documents` table.
20
+ */
21
+ collections?: Record<string, CollectionConfig>;
22
+ /** Name of the fallback JSONB table. Default `documents`. */
23
+ documentsTable?: string;
24
+ }
25
+ /**
26
+ * Hybrid Postgres adapter. Unregistered collections live in a shared JSONB
27
+ * `documents` table; registered collections map flat section fields onto typed
28
+ * columns, with a JSONB `extra` column so unmapped fields are never dropped.
29
+ */
30
+ declare class PostgresDataAdapter implements DataAdapter {
31
+ private readonly pool;
32
+ private readonly collections;
33
+ private readonly documentsTable;
34
+ constructor(config?: PostgresAdapterConfig);
35
+ private cfg;
36
+ /** Create the documents table and every registered typed table if missing. */
37
+ migrate(): Promise<void>;
38
+ /** Split a section into typed columns + leftover `extra`. */
39
+ private toTypedRow;
40
+ private fromTypedRow;
41
+ fetchById<T = Record<string, unknown>>(collection: string, id: string): Promise<(T & {
42
+ id: string;
43
+ }) | null>;
44
+ fetchCollection<T = Record<string, unknown>>(collection: string, q?: Query): Promise<(T & {
45
+ id: string;
46
+ })[]>;
47
+ create<T = Record<string, unknown>>(collection: string, data: T): Promise<T & {
48
+ id: string;
49
+ }>;
50
+ createWithId<T = Record<string, unknown>>(collection: string, id: string, data: T): Promise<T & {
51
+ id: string;
52
+ }>;
53
+ update<T = Record<string, unknown>>(collection: string, id: string, data: Partial<T>): Promise<void>;
54
+ upsert<T = Record<string, unknown>>(collection: string, id: string, data: Partial<T>): Promise<void>;
55
+ private write;
56
+ delete(collection: string, id: string): Promise<void>;
57
+ }
58
+
59
+ export { type CollectionConfig, type ColumnType, type PostgresAdapterConfig, PostgresDataAdapter };