@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.
- package/LICENSE +10 -0
- package/README.md +178 -0
- package/dist/adapters/firestore/index.cjs +152 -0
- package/dist/adapters/firestore/index.cjs.map +1 -0
- package/dist/adapters/firestore/index.d.cts +39 -0
- package/dist/adapters/firestore/index.d.ts +39 -0
- package/dist/adapters/firestore/index.js +120 -0
- package/dist/adapters/firestore/index.js.map +1 -0
- package/dist/adapters/postgres/index.cjs +299 -0
- package/dist/adapters/postgres/index.cjs.map +1 -0
- package/dist/adapters/postgres/index.d.cts +59 -0
- package/dist/adapters/postgres/index.d.ts +59 -0
- package/dist/adapters/postgres/index.js +277 -0
- package/dist/adapters/postgres/index.js.map +1 -0
- package/dist/auth/firebase/client/index.cjs +153 -0
- package/dist/auth/firebase/client/index.cjs.map +1 -0
- package/dist/auth/firebase/client/index.d.cts +29 -0
- package/dist/auth/firebase/client/index.d.ts +29 -0
- package/dist/auth/firebase/client/index.js +138 -0
- package/dist/auth/firebase/client/index.js.map +1 -0
- package/dist/auth/firebase/index.cjs +81 -0
- package/dist/auth/firebase/index.cjs.map +1 -0
- package/dist/auth/firebase/index.d.cts +23 -0
- package/dist/auth/firebase/index.d.ts +23 -0
- package/dist/auth/firebase/index.js +46 -0
- package/dist/auth/firebase/index.js.map +1 -0
- package/dist/auth/nextauth/index.cjs +51 -0
- package/dist/auth/nextauth/index.cjs.map +1 -0
- package/dist/auth/nextauth/index.d.cts +30 -0
- package/dist/auth/nextauth/index.d.ts +30 -0
- package/dist/auth/nextauth/index.js +25 -0
- package/dist/auth/nextauth/index.js.map +1 -0
- package/dist/client/index.cjs +1018 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +96 -0
- package/dist/client/index.d.ts +96 -0
- package/dist/client/index.js +994 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.cjs +19 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +122 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/server/index.cjs +128 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +52 -0
- package/dist/server/index.d.ts +52 -0
- package/dist/server/index.js +99 -0
- package/dist/server/index.js.map +1 -0
- package/dist/storage/cloudinary/index.cjs +55 -0
- package/dist/storage/cloudinary/index.cjs.map +1 -0
- package/dist/storage/cloudinary/index.d.cts +17 -0
- package/dist/storage/cloudinary/index.d.ts +17 -0
- package/dist/storage/cloudinary/index.js +30 -0
- package/dist/storage/cloudinary/index.js.map +1 -0
- package/dist/storage/cloudinary/server.cjs +56 -0
- package/dist/storage/cloudinary/server.cjs.map +1 -0
- package/dist/storage/cloudinary/server.d.cts +16 -0
- package/dist/storage/cloudinary/server.d.ts +16 -0
- package/dist/storage/cloudinary/server.js +31 -0
- package/dist/storage/cloudinary/server.js.map +1 -0
- package/dist/storage/local/index.cjs +44 -0
- package/dist/storage/local/index.cjs.map +1 -0
- package/dist/storage/local/index.d.cts +15 -0
- package/dist/storage/local/index.d.ts +15 -0
- package/dist/storage/local/index.js +19 -0
- package/dist/storage/local/index.js.map +1 -0
- package/dist/storage/local/server.cjs +61 -0
- package/dist/storage/local/server.cjs.map +1 -0
- package/dist/storage/local/server.d.cts +16 -0
- package/dist/storage/local/server.d.ts +16 -0
- package/dist/storage/local/server.js +26 -0
- package/dist/storage/local/server.js.map +1 -0
- package/dist/storage/s3/index.cjs +52 -0
- package/dist/storage/s3/index.cjs.map +1 -0
- package/dist/storage/s3/index.d.cts +14 -0
- package/dist/storage/s3/index.d.ts +14 -0
- package/dist/storage/s3/index.js +27 -0
- package/dist/storage/s3/index.js.map +1 -0
- package/dist/storage/s3/server.cjs +61 -0
- package/dist/storage/s3/server.cjs.map +1 -0
- package/dist/storage/s3/server.d.cts +19 -0
- package/dist/storage/s3/server.d.ts +19 -0
- package/dist/storage/s3/server.js +36 -0
- package/dist/storage/s3/server.js.map +1 -0
- package/package.json +165 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
17
|
+
var __objRest = (source, exclude) => {
|
|
18
|
+
var target = {};
|
|
19
|
+
for (var prop in source)
|
|
20
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
21
|
+
target[prop] = source[prop];
|
|
22
|
+
if (source != null && __getOwnPropSymbols)
|
|
23
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
24
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
25
|
+
target[prop] = source[prop];
|
|
26
|
+
}
|
|
27
|
+
return target;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/adapters/postgres/index.ts
|
|
31
|
+
import { Pool } from "pg";
|
|
32
|
+
import { randomUUID } from "crypto";
|
|
33
|
+
var OP_MAP = {
|
|
34
|
+
eq: "=",
|
|
35
|
+
lt: "<",
|
|
36
|
+
lte: "<=",
|
|
37
|
+
gt: ">",
|
|
38
|
+
gte: ">=",
|
|
39
|
+
in: "= ANY"
|
|
40
|
+
};
|
|
41
|
+
var SQL_TYPE = {
|
|
42
|
+
text: "text",
|
|
43
|
+
int: "integer",
|
|
44
|
+
float: "double precision",
|
|
45
|
+
bool: "boolean",
|
|
46
|
+
date: "date",
|
|
47
|
+
timestamptz: "timestamptz",
|
|
48
|
+
jsonb: "jsonb"
|
|
49
|
+
};
|
|
50
|
+
var PostgresDataAdapter = class {
|
|
51
|
+
constructor(config = {}) {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
if (config.pool) {
|
|
54
|
+
this.pool = config.pool;
|
|
55
|
+
} else if (config.connectionString) {
|
|
56
|
+
this.pool = new Pool({ connectionString: config.connectionString });
|
|
57
|
+
} else {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"PostgresDataAdapter requires either `pool` or `connectionString`."
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
this.collections = (_a = config.collections) != null ? _a : {};
|
|
63
|
+
this.documentsTable = (_b = config.documentsTable) != null ? _b : "documents";
|
|
64
|
+
}
|
|
65
|
+
cfg(collection) {
|
|
66
|
+
return this.collections[collection];
|
|
67
|
+
}
|
|
68
|
+
/** Create the documents table and every registered typed table if missing. */
|
|
69
|
+
async migrate() {
|
|
70
|
+
await this.pool.query(`
|
|
71
|
+
CREATE TABLE IF NOT EXISTS ${ident(this.documentsTable)} (
|
|
72
|
+
id text NOT NULL,
|
|
73
|
+
collection text NOT NULL,
|
|
74
|
+
data jsonb NOT NULL,
|
|
75
|
+
created_at timestamptz DEFAULT now(),
|
|
76
|
+
updated_at timestamptz DEFAULT now(),
|
|
77
|
+
PRIMARY KEY (collection, id)
|
|
78
|
+
);
|
|
79
|
+
`);
|
|
80
|
+
await this.pool.query(
|
|
81
|
+
`CREATE INDEX IF NOT EXISTS ${ident(this.documentsTable + "_collection_created_idx")}
|
|
82
|
+
ON ${ident(this.documentsTable)} (collection, created_at DESC);`
|
|
83
|
+
);
|
|
84
|
+
for (const cfg of Object.values(this.collections)) {
|
|
85
|
+
const cols = Object.entries(cfg.columns).map(([name, type]) => `${ident(name)} ${SQL_TYPE[type]}`).join(",\n ");
|
|
86
|
+
await this.pool.query(`
|
|
87
|
+
CREATE TABLE IF NOT EXISTS ${ident(cfg.table)} (
|
|
88
|
+
id text PRIMARY KEY,
|
|
89
|
+
${cols ? cols + "," : ""}
|
|
90
|
+
extra jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
91
|
+
created_at timestamptz DEFAULT now(),
|
|
92
|
+
updated_at timestamptz DEFAULT now()
|
|
93
|
+
);
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// --- row <-> section mapping ------------------------------------------------
|
|
98
|
+
/** Split a section into typed columns + leftover `extra`. */
|
|
99
|
+
toTypedRow(cfg, data) {
|
|
100
|
+
const known = {};
|
|
101
|
+
const extra = {};
|
|
102
|
+
for (const [key, value] of Object.entries(data)) {
|
|
103
|
+
if (key === "id" || key === "collection") continue;
|
|
104
|
+
if (key in cfg.columns) known[key] = value;
|
|
105
|
+
else extra[key] = value;
|
|
106
|
+
}
|
|
107
|
+
return { known, extra };
|
|
108
|
+
}
|
|
109
|
+
fromTypedRow(collection, cfg, row) {
|
|
110
|
+
const _a = row, { id, extra, created_at, updated_at } = _a, rest = __objRest(_a, ["id", "extra", "created_at", "updated_at"]);
|
|
111
|
+
void created_at;
|
|
112
|
+
void updated_at;
|
|
113
|
+
return __spreadValues(__spreadValues({
|
|
114
|
+
id,
|
|
115
|
+
collection
|
|
116
|
+
}, rest), extra != null ? extra : {});
|
|
117
|
+
}
|
|
118
|
+
// --- reads -----------------------------------------------------------------
|
|
119
|
+
async fetchById(collection, id) {
|
|
120
|
+
const cfg = this.cfg(collection);
|
|
121
|
+
if (cfg) {
|
|
122
|
+
const { rows: rows2 } = await this.pool.query(
|
|
123
|
+
`SELECT * FROM ${ident(cfg.table)} WHERE id = $1`,
|
|
124
|
+
[id]
|
|
125
|
+
);
|
|
126
|
+
if (!rows2[0]) return null;
|
|
127
|
+
return this.fromTypedRow(collection, cfg, rows2[0]);
|
|
128
|
+
}
|
|
129
|
+
const { rows } = await this.pool.query(
|
|
130
|
+
`SELECT id, data FROM ${ident(this.documentsTable)} WHERE id = $1 AND collection = $2`,
|
|
131
|
+
[id, collection]
|
|
132
|
+
);
|
|
133
|
+
if (!rows[0]) return null;
|
|
134
|
+
return __spreadValues({ id: rows[0].id, collection }, rows[0].data);
|
|
135
|
+
}
|
|
136
|
+
async fetchCollection(collection, q) {
|
|
137
|
+
var _a, _b;
|
|
138
|
+
const cfg = this.cfg(collection);
|
|
139
|
+
const params = [];
|
|
140
|
+
const where = [];
|
|
141
|
+
const colExpr = (field) => {
|
|
142
|
+
if (cfg) {
|
|
143
|
+
return field in cfg.columns ? ident(field) : `extra->>${literal(field)}`;
|
|
144
|
+
}
|
|
145
|
+
return `data->>${literal(field)}`;
|
|
146
|
+
};
|
|
147
|
+
if (!cfg) {
|
|
148
|
+
params.push(collection);
|
|
149
|
+
where.push(`collection = $${params.length}`);
|
|
150
|
+
}
|
|
151
|
+
for (const f of (_a = q == null ? void 0 : q.filters) != null ? _a : []) {
|
|
152
|
+
params.push(f.value);
|
|
153
|
+
if (f.op === "in") {
|
|
154
|
+
where.push(`${colExpr(f.field)} = ANY($${params.length})`);
|
|
155
|
+
} else {
|
|
156
|
+
where.push(`${colExpr(f.field)} ${OP_MAP[f.op]} $${params.length}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
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";
|
|
160
|
+
const limit = (q == null ? void 0 : q.limit) != null ? ` LIMIT ${Number(q.limit)}` : "";
|
|
161
|
+
const whereSql = where.length ? ` WHERE ${where.join(" AND ")}` : "";
|
|
162
|
+
const table = cfg ? cfg.table : this.documentsTable;
|
|
163
|
+
const select = cfg ? "*" : "id, data";
|
|
164
|
+
const { rows } = await this.pool.query(
|
|
165
|
+
`SELECT ${select} FROM ${ident(table)}${whereSql} ORDER BY ${orderBy}${limit}`,
|
|
166
|
+
params
|
|
167
|
+
);
|
|
168
|
+
if (cfg) {
|
|
169
|
+
return rows.map((r) => this.fromTypedRow(collection, cfg, r));
|
|
170
|
+
}
|
|
171
|
+
return rows.map((r) => __spreadValues({ id: r.id, collection }, r.data));
|
|
172
|
+
}
|
|
173
|
+
// --- writes ----------------------------------------------------------------
|
|
174
|
+
async create(collection, data) {
|
|
175
|
+
return this.createWithId(collection, randomUUID(), data);
|
|
176
|
+
}
|
|
177
|
+
async createWithId(collection, id, data) {
|
|
178
|
+
const cfg = this.cfg(collection);
|
|
179
|
+
const record = data;
|
|
180
|
+
if (cfg) {
|
|
181
|
+
const { known, extra } = this.toTypedRow(cfg, record);
|
|
182
|
+
const cols = ["id", ...Object.keys(known), "extra"];
|
|
183
|
+
const vals = [id, ...Object.values(known), JSON.stringify(extra)];
|
|
184
|
+
const placeholders = cols.map((_, i) => `$${i + 1}`).join(", ");
|
|
185
|
+
await this.pool.query(
|
|
186
|
+
`INSERT INTO ${ident(cfg.table)} (${cols.map(ident).join(", ")})
|
|
187
|
+
VALUES (${placeholders})`,
|
|
188
|
+
vals
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
await this.pool.query(
|
|
192
|
+
`INSERT INTO ${ident(this.documentsTable)} (id, collection, data)
|
|
193
|
+
VALUES ($1, $2, $3)`,
|
|
194
|
+
[id, collection, JSON.stringify(record)]
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return __spreadValues({ id }, data);
|
|
198
|
+
}
|
|
199
|
+
async update(collection, id, data) {
|
|
200
|
+
await this.write(collection, id, data, "update");
|
|
201
|
+
}
|
|
202
|
+
async upsert(collection, id, data) {
|
|
203
|
+
await this.write(collection, id, data, "upsert");
|
|
204
|
+
}
|
|
205
|
+
async write(collection, id, data, mode) {
|
|
206
|
+
const cfg = this.cfg(collection);
|
|
207
|
+
if (cfg) {
|
|
208
|
+
const { known, extra } = this.toTypedRow(cfg, data);
|
|
209
|
+
const setCols = Object.keys(known);
|
|
210
|
+
const setExpr = setCols.map((c, i) => `${ident(c)} = $${i + 2}`).join(", ");
|
|
211
|
+
const extraIdx = setCols.length + 2;
|
|
212
|
+
const sets = [
|
|
213
|
+
setExpr,
|
|
214
|
+
// Table-qualified: in ON CONFLICT DO UPDATE both the target table and the
|
|
215
|
+
// `excluded` pseudo-relation expose `extra`, so a bare ref is ambiguous.
|
|
216
|
+
`extra = ${ident(cfg.table)}.${ident("extra")} || $${extraIdx}::jsonb`,
|
|
217
|
+
`updated_at = now()`
|
|
218
|
+
].filter(Boolean).join(", ");
|
|
219
|
+
const params = [id, ...Object.values(known), JSON.stringify(extra)];
|
|
220
|
+
if (mode === "upsert") {
|
|
221
|
+
const insertCols = ["id", ...setCols, "extra"];
|
|
222
|
+
const insertVals = params;
|
|
223
|
+
const placeholders = insertCols.map((_, i) => `$${i + 1}`).join(", ");
|
|
224
|
+
await this.pool.query(
|
|
225
|
+
`INSERT INTO ${ident(cfg.table)} (${insertCols.map(ident).join(", ")})
|
|
226
|
+
VALUES (${placeholders})
|
|
227
|
+
ON CONFLICT (id) DO UPDATE SET ${sets}`,
|
|
228
|
+
insertVals
|
|
229
|
+
);
|
|
230
|
+
} else {
|
|
231
|
+
await this.pool.query(
|
|
232
|
+
`UPDATE ${ident(cfg.table)} SET ${sets} WHERE id = $1`,
|
|
233
|
+
params
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (mode === "upsert") {
|
|
239
|
+
await this.pool.query(
|
|
240
|
+
`INSERT INTO ${ident(this.documentsTable)} (id, collection, data)
|
|
241
|
+
VALUES ($1, $2, $3)
|
|
242
|
+
ON CONFLICT (collection, id) DO UPDATE
|
|
243
|
+
SET data = ${ident(this.documentsTable)}.data || $3::jsonb,
|
|
244
|
+
updated_at = now()`,
|
|
245
|
+
[id, collection, JSON.stringify(data)]
|
|
246
|
+
);
|
|
247
|
+
} else {
|
|
248
|
+
await this.pool.query(
|
|
249
|
+
`UPDATE ${ident(this.documentsTable)}
|
|
250
|
+
SET data = data || $2::jsonb, updated_at = now()
|
|
251
|
+
WHERE id = $1 AND collection = $3`,
|
|
252
|
+
[id, JSON.stringify(data), collection]
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async delete(collection, id) {
|
|
257
|
+
const cfg = this.cfg(collection);
|
|
258
|
+
if (cfg) {
|
|
259
|
+
await this.pool.query(`DELETE FROM ${ident(cfg.table)} WHERE id = $1`, [id]);
|
|
260
|
+
} else {
|
|
261
|
+
await this.pool.query(
|
|
262
|
+
`DELETE FROM ${ident(this.documentsTable)} WHERE id = $1 AND collection = $2`,
|
|
263
|
+
[id, collection]
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
function ident(name) {
|
|
269
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
270
|
+
}
|
|
271
|
+
function literal(value) {
|
|
272
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
273
|
+
}
|
|
274
|
+
export {
|
|
275
|
+
PostgresDataAdapter
|
|
276
|
+
};
|
|
277
|
+
//# sourceMappingURL=index.js.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,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/auth/firebase/client/index.tsx
|
|
22
|
+
var client_exports = {};
|
|
23
|
+
__export(client_exports, {
|
|
24
|
+
FirebaseAuthProvider: () => FirebaseAuthProvider,
|
|
25
|
+
useFirebaseAuth: () => useFirebaseAuth
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(client_exports);
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
var import_auth = require("firebase/auth");
|
|
30
|
+
var import_client = require("@dalgoridim/headless-cms/client");
|
|
31
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
+
var FirebaseAuthContext = (0, import_react.createContext)(
|
|
33
|
+
void 0
|
|
34
|
+
);
|
|
35
|
+
function setCookie(name, value) {
|
|
36
|
+
document.cookie = `${name}=${encodeURIComponent(value)}; path=/`;
|
|
37
|
+
}
|
|
38
|
+
function deleteCookie(name) {
|
|
39
|
+
document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
40
|
+
}
|
|
41
|
+
function FirebaseAuthProvider({
|
|
42
|
+
children,
|
|
43
|
+
auth,
|
|
44
|
+
googleProvider,
|
|
45
|
+
cookieName = "adminToken",
|
|
46
|
+
onLogout
|
|
47
|
+
}) {
|
|
48
|
+
const [user, setUser] = (0, import_react.useState)(null);
|
|
49
|
+
const [isAdmin, setIsAdmin] = (0, import_react.useState)(false);
|
|
50
|
+
const [isEditing, setIsEditing] = (0, import_react.useState)(false);
|
|
51
|
+
(0, import_react.useEffect)(() => {
|
|
52
|
+
const unsubscribe = (0, import_auth.onAuthStateChanged)(auth, async (u) => {
|
|
53
|
+
if (!u) {
|
|
54
|
+
setUser(null);
|
|
55
|
+
setIsAdmin(false);
|
|
56
|
+
deleteCookie(cookieName);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const tokenResult = await u.getIdTokenResult();
|
|
60
|
+
const admin = !!tokenResult.claims.admin;
|
|
61
|
+
if (!admin) {
|
|
62
|
+
await (0, import_auth.signOut)(auth);
|
|
63
|
+
setUser(null);
|
|
64
|
+
setIsAdmin(false);
|
|
65
|
+
deleteCookie(cookieName);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setUser(u);
|
|
69
|
+
setIsAdmin(true);
|
|
70
|
+
setCookie(cookieName, tokenResult.token);
|
|
71
|
+
});
|
|
72
|
+
return () => unsubscribe();
|
|
73
|
+
}, [auth, cookieName]);
|
|
74
|
+
(0, import_react.useEffect)(() => {
|
|
75
|
+
const originalFetch = window.fetch;
|
|
76
|
+
window.fetch = async (...args) => {
|
|
77
|
+
const response = await originalFetch(...args);
|
|
78
|
+
if (response.status === 401) {
|
|
79
|
+
try {
|
|
80
|
+
const data = await response.clone().json();
|
|
81
|
+
if (data == null ? void 0 : data.logout) {
|
|
82
|
+
await (0, import_auth.signOut)(auth);
|
|
83
|
+
setUser(null);
|
|
84
|
+
setIsAdmin(false);
|
|
85
|
+
setIsEditing(false);
|
|
86
|
+
deleteCookie(cookieName);
|
|
87
|
+
onLogout == null ? void 0 : onLogout();
|
|
88
|
+
}
|
|
89
|
+
} catch (e) {
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return response;
|
|
93
|
+
};
|
|
94
|
+
return () => {
|
|
95
|
+
window.fetch = originalFetch;
|
|
96
|
+
};
|
|
97
|
+
}, [auth, cookieName, onLogout]);
|
|
98
|
+
const requireAdminClaim = async (u) => {
|
|
99
|
+
const tokenResult = await u.getIdTokenResult();
|
|
100
|
+
if (!tokenResult.claims.admin) {
|
|
101
|
+
await (0, import_auth.signOut)(auth);
|
|
102
|
+
throw new Error("Unauthorized");
|
|
103
|
+
}
|
|
104
|
+
setUser(u);
|
|
105
|
+
setIsAdmin(true);
|
|
106
|
+
setCookie(cookieName, tokenResult.token);
|
|
107
|
+
};
|
|
108
|
+
const loginWithGoogle = async () => {
|
|
109
|
+
if (!googleProvider)
|
|
110
|
+
throw new Error("FirebaseAuthProvider: googleProvider not configured");
|
|
111
|
+
const result = await (0, import_auth.signInWithPopup)(auth, googleProvider);
|
|
112
|
+
await requireAdminClaim(result.user);
|
|
113
|
+
};
|
|
114
|
+
const loginWithEmail = async (email, password) => {
|
|
115
|
+
const result = await (0, import_auth.signInWithEmailAndPassword)(auth, email, password);
|
|
116
|
+
await requireAdminClaim(result.user);
|
|
117
|
+
};
|
|
118
|
+
const logout = async () => {
|
|
119
|
+
await (0, import_auth.signOut)(auth);
|
|
120
|
+
setUser(null);
|
|
121
|
+
setIsAdmin(false);
|
|
122
|
+
setIsEditing(false);
|
|
123
|
+
deleteCookie(cookieName);
|
|
124
|
+
};
|
|
125
|
+
const toggleEdit = () => setIsEditing((p) => !p);
|
|
126
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
127
|
+
FirebaseAuthContext.Provider,
|
|
128
|
+
{
|
|
129
|
+
value: {
|
|
130
|
+
user,
|
|
131
|
+
isAdmin,
|
|
132
|
+
isEditing,
|
|
133
|
+
toggleEdit,
|
|
134
|
+
loginWithGoogle,
|
|
135
|
+
loginWithEmail,
|
|
136
|
+
logout
|
|
137
|
+
},
|
|
138
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_client.CmsAuthProvider, { value: { isAdmin, isEditing, toggleEdit }, children })
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
function useFirebaseAuth() {
|
|
143
|
+
const ctx = (0, import_react.useContext)(FirebaseAuthContext);
|
|
144
|
+
if (!ctx)
|
|
145
|
+
throw new Error("useFirebaseAuth must be used within a FirebaseAuthProvider");
|
|
146
|
+
return ctx;
|
|
147
|
+
}
|
|
148
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
149
|
+
0 && (module.exports = {
|
|
150
|
+
FirebaseAuthProvider,
|
|
151
|
+
useFirebaseAuth
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/auth/firebase/client/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n signInWithPopup,\n signInWithEmailAndPassword,\n signOut,\n onAuthStateChanged,\n type Auth,\n type User,\n type GoogleAuthProvider,\n} from \"firebase/auth\";\n// Imported via the public specifier so we share the SAME context instance as\n// the consumer's edit primitives at runtime (see tsup `external` + tsconfig paths).\nimport { CmsAuthProvider } from \"@dalgoridim/headless-cms/client\";\n\nexport interface FirebaseAuthProviderProps {\n children: ReactNode;\n /** A Firebase `Auth` instance from the consumer's app config. */\n auth: Auth;\n /** Optional Google provider for `loginWithGoogle`. */\n googleProvider?: GoogleAuthProvider;\n /** Cookie name for the ID token. Default `adminToken`. */\n cookieName?: string;\n /** Called when a 401 `{ logout: true }` response is intercepted. */\n onLogout?: () => void;\n}\n\ninterface FirebaseAuthContextValue {\n user: User | null;\n isAdmin: boolean;\n isEditing: boolean;\n toggleEdit: () => void;\n loginWithGoogle: () => Promise<void>;\n loginWithEmail: (email: string, password: string) => Promise<void>;\n logout: () => Promise<void>;\n}\n\nconst FirebaseAuthContext = createContext<FirebaseAuthContextValue | undefined>(\n undefined,\n);\n\nfunction setCookie(name: string, value: string) {\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/`;\n}\nfunction deleteCookie(name: string) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\n\nexport function FirebaseAuthProvider({\n children,\n auth,\n googleProvider,\n cookieName = \"adminToken\",\n onLogout,\n}: FirebaseAuthProviderProps) {\n const [user, setUser] = useState<User | null>(null);\n const [isAdmin, setIsAdmin] = useState(false);\n const [isEditing, setIsEditing] = useState(false);\n\n useEffect(() => {\n const unsubscribe = onAuthStateChanged(auth, async (u) => {\n if (!u) {\n setUser(null);\n setIsAdmin(false);\n deleteCookie(cookieName);\n return;\n }\n const tokenResult = await u.getIdTokenResult();\n const admin = !!tokenResult.claims.admin;\n if (!admin) {\n await signOut(auth);\n setUser(null);\n setIsAdmin(false);\n deleteCookie(cookieName);\n return;\n }\n setUser(u);\n setIsAdmin(true);\n setCookie(cookieName, tokenResult.token);\n });\n return () => unsubscribe();\n }, [auth, cookieName]);\n\n // Intercept admin 401s so an expired session forces sign-out.\n useEffect(() => {\n const originalFetch = window.fetch;\n window.fetch = async (...args: Parameters<typeof fetch>) => {\n const response = await originalFetch(...args);\n if (response.status === 401) {\n try {\n const data = await response.clone().json();\n if (data?.logout) {\n await signOut(auth);\n setUser(null);\n setIsAdmin(false);\n setIsEditing(false);\n deleteCookie(cookieName);\n onLogout?.();\n }\n } catch {\n /* not a JSON body — ignore */\n }\n }\n return response;\n };\n return () => {\n window.fetch = originalFetch;\n };\n }, [auth, cookieName, onLogout]);\n\n const requireAdminClaim = async (u: User) => {\n const tokenResult = await u.getIdTokenResult();\n if (!tokenResult.claims.admin) {\n await signOut(auth);\n throw new Error(\"Unauthorized\");\n }\n setUser(u);\n setIsAdmin(true);\n setCookie(cookieName, tokenResult.token);\n };\n\n const loginWithGoogle = async () => {\n if (!googleProvider)\n throw new Error(\"FirebaseAuthProvider: googleProvider not configured\");\n const result = await signInWithPopup(auth, googleProvider);\n await requireAdminClaim(result.user);\n };\n\n const loginWithEmail = async (email: string, password: string) => {\n const result = await signInWithEmailAndPassword(auth, email, password);\n await requireAdminClaim(result.user);\n };\n\n const logout = async () => {\n await signOut(auth);\n setUser(null);\n setIsAdmin(false);\n setIsEditing(false);\n deleteCookie(cookieName);\n };\n\n const toggleEdit = () => setIsEditing((p) => !p);\n\n return (\n <FirebaseAuthContext.Provider\n value={{\n user,\n isAdmin,\n isEditing,\n toggleEdit,\n loginWithGoogle,\n loginWithEmail,\n logout,\n }}\n >\n <CmsAuthProvider value={{ isAdmin, isEditing, toggleEdit }}>\n {children}\n </CmsAuthProvider>\n </FirebaseAuthContext.Provider>\n );\n}\n\n/** Extended Firebase auth API (login/logout/user) for login pages and toolbars. */\nexport function useFirebaseAuth(): FirebaseAuthContextValue {\n const ctx = useContext(FirebaseAuthContext);\n if (!ctx)\n throw new Error(\"useFirebaseAuth must be used within a FirebaseAuthProvider\");\n return ctx;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { Auth, GoogleAuthProvider, User } from 'firebase/auth';
|
|
4
|
+
|
|
5
|
+
interface FirebaseAuthProviderProps {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
/** A Firebase `Auth` instance from the consumer's app config. */
|
|
8
|
+
auth: Auth;
|
|
9
|
+
/** Optional Google provider for `loginWithGoogle`. */
|
|
10
|
+
googleProvider?: GoogleAuthProvider;
|
|
11
|
+
/** Cookie name for the ID token. Default `adminToken`. */
|
|
12
|
+
cookieName?: string;
|
|
13
|
+
/** Called when a 401 `{ logout: true }` response is intercepted. */
|
|
14
|
+
onLogout?: () => void;
|
|
15
|
+
}
|
|
16
|
+
interface FirebaseAuthContextValue {
|
|
17
|
+
user: User | null;
|
|
18
|
+
isAdmin: boolean;
|
|
19
|
+
isEditing: boolean;
|
|
20
|
+
toggleEdit: () => void;
|
|
21
|
+
loginWithGoogle: () => Promise<void>;
|
|
22
|
+
loginWithEmail: (email: string, password: string) => Promise<void>;
|
|
23
|
+
logout: () => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
declare function FirebaseAuthProvider({ children, auth, googleProvider, cookieName, onLogout, }: FirebaseAuthProviderProps): React.JSX.Element;
|
|
26
|
+
/** Extended Firebase auth API (login/logout/user) for login pages and toolbars. */
|
|
27
|
+
declare function useFirebaseAuth(): FirebaseAuthContextValue;
|
|
28
|
+
|
|
29
|
+
export { FirebaseAuthProvider, type FirebaseAuthProviderProps, useFirebaseAuth };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { Auth, GoogleAuthProvider, User } from 'firebase/auth';
|
|
4
|
+
|
|
5
|
+
interface FirebaseAuthProviderProps {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
/** A Firebase `Auth` instance from the consumer's app config. */
|
|
8
|
+
auth: Auth;
|
|
9
|
+
/** Optional Google provider for `loginWithGoogle`. */
|
|
10
|
+
googleProvider?: GoogleAuthProvider;
|
|
11
|
+
/** Cookie name for the ID token. Default `adminToken`. */
|
|
12
|
+
cookieName?: string;
|
|
13
|
+
/** Called when a 401 `{ logout: true }` response is intercepted. */
|
|
14
|
+
onLogout?: () => void;
|
|
15
|
+
}
|
|
16
|
+
interface FirebaseAuthContextValue {
|
|
17
|
+
user: User | null;
|
|
18
|
+
isAdmin: boolean;
|
|
19
|
+
isEditing: boolean;
|
|
20
|
+
toggleEdit: () => void;
|
|
21
|
+
loginWithGoogle: () => Promise<void>;
|
|
22
|
+
loginWithEmail: (email: string, password: string) => Promise<void>;
|
|
23
|
+
logout: () => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
declare function FirebaseAuthProvider({ children, auth, googleProvider, cookieName, onLogout, }: FirebaseAuthProviderProps): React.JSX.Element;
|
|
26
|
+
/** Extended Firebase auth API (login/logout/user) for login pages and toolbars. */
|
|
27
|
+
declare function useFirebaseAuth(): FirebaseAuthContextValue;
|
|
28
|
+
|
|
29
|
+
export { FirebaseAuthProvider, type FirebaseAuthProviderProps, useFirebaseAuth };
|