@auersperg/flare 0.3.1 → 0.3.4

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 (52) hide show
  1. package/README.md +13 -8
  2. package/dist/cli/{chunk-x14smx1y.js → chunk-5s6zv1h9.js} +1 -1
  3. package/dist/cli/chunk-5tjevcsj.js +1174 -0
  4. package/dist/cli/{chunk-5cq8wgte.js → chunk-5yg1zj5n.js} +48 -29
  5. package/dist/cli/{chunk-786w6vrj.js → chunk-fv98dfg5.js} +21 -1
  6. package/dist/cli/flare.js +120 -4
  7. package/dist/platform/admin.js +790 -0
  8. package/dist/platform/dashboard/_astro/auth-view.CuxQ12Fa.js +1 -0
  9. package/dist/platform/dashboard/_astro/badge.DwKWvVfO.js +1 -0
  10. package/dist/platform/dashboard/_astro/card.Bdqmram9.js +1 -0
  11. package/dist/platform/dashboard/_astro/cli-approval.VKPc8wYS.js +1 -0
  12. package/dist/platform/dashboard/_astro/client.BUUZgEFj.js +1 -0
  13. package/dist/platform/dashboard/_astro/client.DYdPL2z4.js +9 -0
  14. package/dist/platform/dashboard/_astro/common.HrRZXwD9.js +1 -0
  15. package/dist/platform/dashboard/_astro/dashboard.CH594XTw.js +3 -0
  16. package/dist/platform/dashboard/_astro/data-view.CcyUYvId.js +1 -0
  17. package/dist/platform/dashboard/_astro/function-badge.MlTRYkqM.js +1 -0
  18. package/dist/platform/dashboard/_astro/functions-view.C8JWdNAh.js +1 -0
  19. package/dist/platform/dashboard/_astro/geist-cyrillic-ext-wght-normal.DjL33-gN.woff2 +0 -0
  20. package/dist/platform/dashboard/_astro/geist-cyrillic-wght-normal.BEAKL7Jp.woff2 +0 -0
  21. package/dist/platform/dashboard/_astro/geist-latin-ext-wght-normal.DC-KSUi6.woff2 +0 -0
  22. package/dist/platform/dashboard/_astro/geist-latin-wght-normal.BgDaEnEv.woff2 +0 -0
  23. package/dist/platform/dashboard/_astro/geist-vietnamese-wght-normal.6IgcOCM7.woff2 +0 -0
  24. package/dist/platform/dashboard/_astro/index.DiiMDa7R.css +2 -0
  25. package/dist/platform/dashboard/_astro/input-group.3twGpQ77.js +1 -0
  26. package/dist/platform/dashboard/_astro/react-dom.DwX89IcG.js +1 -0
  27. package/dist/platform/dashboard/_astro/refresh-cw.B0Cj85jJ.js +1 -0
  28. package/dist/platform/dashboard/_astro/sdk-view.CRFkbEyS.js +1 -0
  29. package/dist/platform/dashboard/_astro/services-view.B2suYnaG.js +1 -0
  30. package/dist/platform/dashboard/_astro/settings-view.BFP47ySK.js +3 -0
  31. package/dist/platform/dashboard/_astro/status-badge.BV1dzbX9.js +1 -0
  32. package/dist/platform/dashboard/_astro/table-guide.DzBDP6fC.js +2 -0
  33. package/dist/platform/dashboard/_astro/tabs.GRO5rCrK.js +1 -0
  34. package/dist/platform/dashboard/_headers +3 -0
  35. package/dist/platform/dashboard/bootstrap/action.js +263 -0
  36. package/dist/platform/dashboard/bootstrap/api.ts +5 -0
  37. package/dist/platform/dashboard/bootstrap/facet.js +914 -0
  38. package/dist/platform/dashboard/bootstrap/manifest.json +5 -0
  39. package/dist/platform/dashboard/bootstrap/openapi.json +18 -0
  40. package/dist/platform/dashboard/bootstrap/release.json +11 -0
  41. package/dist/platform/dashboard/bootstrap/schema.json +1 -0
  42. package/dist/platform/dashboard/index.html +1 -0
  43. package/dist/platform/manifest.json +230 -0
  44. package/dist/platform/migrations/0001.sql +8 -0
  45. package/dist/platform/migrations/0002_project_aliases.sql +6 -0
  46. package/dist/platform/migrations/0003_cli_login.sql +9 -0
  47. package/dist/platform/migrations/0004_retention.sql +4 -0
  48. package/dist/platform/migrations/0005_project_deletion.sql +11 -0
  49. package/dist/platform/migrations/0006_platform_installation.sql +8 -0
  50. package/dist/platform/runtime.js +9392 -0
  51. package/dist/sdk/client.js +10 -2
  52. package/package.json +1 -1
@@ -0,0 +1,914 @@
1
+ // packages/flare/dist/sdk/protocol.js
2
+ var LIMITS = Object.freeze({
3
+ inputBytes: 65536,
4
+ outputBytes: 262144,
5
+ frameBytes: 8192,
6
+ subscriptions: 64,
7
+ fileBytes: 10485760
8
+ });
9
+
10
+ class FlareError extends Error {
11
+ code;
12
+ ambiguous;
13
+ constructor(code, message, ambiguous = false) {
14
+ super(message);
15
+ this.code = code;
16
+ this.ambiguous = ambiguous;
17
+ this.name = "FlareError";
18
+ }
19
+ }
20
+ function errorCode(error) {
21
+ if (error instanceof Error && ["FlareError", "FlaredError"].includes(error.name)) {
22
+ const code = error.code;
23
+ if (typeof code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(code))
24
+ return code;
25
+ }
26
+ return "OPERATION_FAILED";
27
+ }
28
+ function assertPath(path) {
29
+ if (path.length > 256 || !/^[A-Za-z][A-Za-z0-9_]*(\/[A-Za-z][A-Za-z0-9_]*)*$/.test(path) || path.split("/").some((p) => ["__proto__", "prototype", "constructor"].includes(p))) {
30
+ throw new FlareError("INVALID_PATH", "Invalid function path");
31
+ }
32
+ }
33
+ function stableJson(value) {
34
+ const ancestors = new Set;
35
+ function visit(v, depth) {
36
+ if (depth > 40)
37
+ throw new FlareError("INVALID_JSON", "JSON nesting limit exceeded");
38
+ if (v === null || typeof v === "string" || typeof v === "boolean")
39
+ return v;
40
+ if (typeof v === "number" && Number.isFinite(v))
41
+ return v;
42
+ if (typeof v !== "object" || v === null || ancestors.has(v))
43
+ throw new FlareError("INVALID_JSON", "Expected lossless JSON");
44
+ if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null) {
45
+ throw new FlareError("INVALID_JSON", "Expected plain JSON object");
46
+ }
47
+ if (Object.getOwnPropertySymbols(v).length)
48
+ throw new FlareError("INVALID_JSON", "Symbol keys are unsupported");
49
+ ancestors.add(v);
50
+ let result;
51
+ if (Array.isArray(v)) {
52
+ result = Array.from(v, (item) => visit(item, depth + 1));
53
+ } else {
54
+ const out = Object.create(null);
55
+ for (const key of Object.keys(v).sort()) {
56
+ const descriptor = Object.getOwnPropertyDescriptor(v, key);
57
+ if (!("value" in descriptor))
58
+ throw new FlareError("INVALID_JSON", "Getters are unsupported");
59
+ out[key] = visit(descriptor.value, depth + 1);
60
+ }
61
+ result = out;
62
+ }
63
+ ancestors.delete(v);
64
+ return result;
65
+ }
66
+ return JSON.stringify(visit(value, 0));
67
+ }
68
+ function boundedJson(value, maxBytes) {
69
+ const json = stableJson(value);
70
+ if (new TextEncoder().encode(json).byteLength > maxBytes)
71
+ throw new FlareError("TOO_LARGE", "JSON payload exceeds limit");
72
+ return json;
73
+ }
74
+
75
+ // packages/flare/dist/sdk/schema.js
76
+ function identifier(name) {
77
+ if (!/^[a-zA-Z][a-zA-Z0-9_]{0,47}$/.test(name) || name.toLowerCase().startsWith("sqlite_"))
78
+ throw new FlareError("INVALID_SCHEMA", "Invalid table, index or field name");
79
+ return name;
80
+ }
81
+ var quote = (name) => {
82
+ if (!/^[a-zA-Z][a-zA-Z0-9_]{0,127}$/.test(name))
83
+ throw new FlareError("INVALID_SCHEMA", "Invalid SQL identifier");
84
+ return `"${name}"`;
85
+ };
86
+ var fieldSql = (field) => field === "_id" || field === "_creationTime" ? `"${field}"` : `json_extract("_data", '$.${identifier(field)}')`;
87
+ var indexName = (table, index) => `idx_${table.length}_${table}_${index}`;
88
+ var creationIndex = (table) => `creation_${table}`;
89
+ function schemaContract(schema) {
90
+ const out = Object.create(null);
91
+ if (Object.keys(schema).length > 100)
92
+ throw new FlareError("INVALID_SCHEMA", "Maximum 100 tables");
93
+ for (const [name, table] of Object.entries(schema)) {
94
+ identifier(name);
95
+ const document = table.validator["~standard"].jsonSchema.output({
96
+ target: "draft-2020-12"
97
+ });
98
+ out[name] = { document, indexes: table.indexes };
99
+ }
100
+ assertSchemaContract(out);
101
+ return out;
102
+ }
103
+ function assertSchemaContract(contract) {
104
+ boundedJson(contract, 1048576);
105
+ if (!contract || Array.isArray(contract) || typeof contract !== "object" || Object.keys(contract).length > 100)
106
+ throw new FlareError("INVALID_SCHEMA", "Invalid schema");
107
+ const names = new Set;
108
+ for (const [name, table] of Object.entries(contract)) {
109
+ if (names.has(name.toLowerCase()))
110
+ throw new FlareError("INVALID_SCHEMA", "SQLite table names are case-insensitive");
111
+ names.add(name.toLowerCase());
112
+ identifier(name);
113
+ const doc = table?.document;
114
+ if (!doc || doc.type !== "object" || !doc.properties || doc.additionalProperties !== false)
115
+ throw new FlareError("INVALID_SCHEMA", "Tables require a closed object validator");
116
+ for (const field of Object.keys(doc.properties))
117
+ identifier(field);
118
+ if (Object.keys(doc.properties).length > 64 || !table.indexes || Object.keys(table.indexes).length > 20)
119
+ throw new FlareError("INVALID_SCHEMA", "Schema exceeds limits");
120
+ const indexes = new Set;
121
+ for (const [index, fields] of Object.entries(table.indexes)) {
122
+ if (indexes.has(index.toLowerCase()))
123
+ throw new FlareError("INVALID_SCHEMA", "SQLite index names are case-insensitive");
124
+ indexes.add(index.toLowerCase());
125
+ identifier(index);
126
+ if (!Array.isArray(fields) || fields.length < 1 || fields.length > 4 || new Set(fields).size !== fields.length)
127
+ throw new FlareError("INVALID_SCHEMA", "Invalid index fields");
128
+ for (const field of fields) {
129
+ identifier(field);
130
+ const p = doc.properties[field];
131
+ const scalar = (s) => Array.isArray(s?.type) && s.type.every((type) => ["string", "number", "integer", "boolean", "null"].includes(type)) || ["string", "number", "integer", "boolean", "null"].includes(s?.type) || Array.isArray(s?.anyOf) && s.anyOf.every(scalar);
132
+ if (!p || !scalar(p))
133
+ throw new FlareError("INVALID_SCHEMA", "Indexes require declared scalar fields");
134
+ }
135
+ }
136
+ }
137
+ }
138
+ var documents = new WeakMap;
139
+ function documentSchema(table) {
140
+ let doc = documents.get(table.validator);
141
+ if (!doc) {
142
+ doc = table.validator["~standard"].jsonSchema.output({
143
+ target: "draft-2020-12"
144
+ });
145
+ documents.set(table.validator, doc);
146
+ }
147
+ return doc;
148
+ }
149
+ async function documentValue(table, input) {
150
+ boundedJson(input, 65536);
151
+ const properties = documentSchema(table).properties ?? {};
152
+ if (!input || Array.isArray(input) || typeof input !== "object" || Object.keys(input).some((k) => !Object.hasOwn(properties, k)))
153
+ throw new FlareError("INVALID_INPUT", "Unknown document field");
154
+ const result = await table.validator["~standard"].validate(input);
155
+ if (result.issues || !result.value || Array.isArray(result.value) || typeof result.value !== "object" || Object.keys(result.value).some((k) => !Object.hasOwn(properties, k)))
156
+ throw new FlareError("INVALID_INPUT", "Document failed schema validation");
157
+ return JSON.parse(boundedJson(result.value, 65536));
158
+ }
159
+ async function applySchema(storage, schema) {
160
+ const contract = schemaContract(schema), encoded = stableJson(contract);
161
+ await storage.transaction(async () => {
162
+ storage.sql.exec("CREATE TABLE IF NOT EXISTS _flared_schema (id INTEGER PRIMARY KEY CHECK(id=1), contract TEXT NOT NULL)");
163
+ const oldRow = storage.sql.exec("SELECT contract FROM _flared_schema WHERE id=1").toArray()[0];
164
+ if (oldRow?.contract === encoded)
165
+ return;
166
+ const old = oldRow ? JSON.parse(String(oldRow.contract)) : {};
167
+ for (const name of Object.keys(old))
168
+ if (!Object.hasOwn(schema, name)) {
169
+ if (storage.sql.exec(`SELECT 1 FROM ${quote(name)} LIMIT 1`).toArray().length)
170
+ throw new FlareError("INVALID_SCHEMA", "Cannot remove a nonempty table; explicitly clear it first");
171
+ storage.sql.exec(`DROP TABLE ${quote(name)}`);
172
+ }
173
+ for (const [name, definition] of Object.entries(contract)) {
174
+ storage.sql.exec(`CREATE TABLE IF NOT EXISTS ${quote(name)} (_id TEXT PRIMARY KEY, _creationTime REAL NOT NULL, _data TEXT NOT NULL CHECK(json_valid(_data)))`);
175
+ let cursor = "";
176
+ while (!old[name] || stableJson(old[name].document) !== stableJson(definition.document)) {
177
+ const rows = storage.sql.exec(`SELECT _id,_data FROM ${quote(name)} WHERE _id > ? ORDER BY _id LIMIT 100`, cursor).toArray();
178
+ for (const row of rows) {
179
+ const value = await documentValue(schema[name], JSON.parse(String(row._data)));
180
+ if (stableJson(value) !== stableJson(JSON.parse(String(row._data))))
181
+ storage.sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=?`, stableJson(value), row._id);
182
+ }
183
+ if (rows.length < 100)
184
+ break;
185
+ cursor = String(rows.at(-1)._id);
186
+ }
187
+ storage.sql.exec(`CREATE INDEX IF NOT EXISTS ${quote(creationIndex(name))} ON ${quote(name)} (_creationTime,_id)`);
188
+ for (const [index, fields] of Object.entries(old[name]?.indexes ?? {}))
189
+ if (stableJson(definition.indexes[index] ?? null) !== stableJson(fields))
190
+ storage.sql.exec(`DROP INDEX IF EXISTS ${quote(indexName(name, index))}`);
191
+ for (const [index, fields] of Object.entries(definition.indexes))
192
+ storage.sql.exec(`CREATE INDEX IF NOT EXISTS ${quote(indexName(name, index))} ON ${quote(name)} (${[...fields.map(fieldSql), "_creationTime", "_id"].join(",")})`);
193
+ }
194
+ storage.sql.exec("INSERT INTO _flared_schema VALUES (1,?) ON CONFLICT(id) DO UPDATE SET contract=excluded.contract", encoded);
195
+ });
196
+ await storage.sync();
197
+ }
198
+
199
+ // packages/flare/dist/sdk/server.js
200
+ function declare(kind) {
201
+ return (options) => Object.freeze({ ...options, kind, auth: options.auth ?? "required" });
202
+ }
203
+ var query = declare("query");
204
+ var mutation = declare("mutation");
205
+ var action = declare("action");
206
+ function defineApp(options) {
207
+ flatten(options.functions);
208
+ schemaContract(options.schema);
209
+ return Object.freeze(options);
210
+ }
211
+ function flatten(tree) {
212
+ const out = Object.create(null);
213
+ const ancestors = new Set;
214
+ function walk(node, prefix, depth) {
215
+ if (depth > 20 || ancestors.has(node))
216
+ throw new FlareError("INVALID_CONTRACT", "Invalid function tree");
217
+ ancestors.add(node);
218
+ for (const [name, value] of Object.entries(node)) {
219
+ const path = prefix ? `${prefix}/${name}` : name;
220
+ assertPath(path);
221
+ if (value && typeof value === "object" && "handler" in value && typeof value.handler === "function") {
222
+ const definition = value;
223
+ if (!["query", "mutation", "action"].includes(definition.kind))
224
+ throw new FlareError("INVALID_CONTRACT", "Invalid function kind");
225
+ if (out[path])
226
+ throw new FlareError("INVALID_CONTRACT", "Duplicate function path");
227
+ out[path] = definition;
228
+ } else if (value && typeof value === "object")
229
+ walk(value, path, depth + 1);
230
+ else
231
+ throw new FlareError("INVALID_CONTRACT", "Invalid function definition");
232
+ }
233
+ ancestors.delete(node);
234
+ }
235
+ walk(tree, "", 0);
236
+ return out;
237
+ }
238
+ async function validate(validator, value, output = false) {
239
+ boundedJson(value, output ? LIMITS.outputBytes : LIMITS.inputBytes);
240
+ const result = await validator["~standard"].validate(value);
241
+ if (result.issues)
242
+ throw new FlareError(output ? "INVALID_OUTPUT" : "INVALID_INPUT", output ? "Function returned invalid data" : "Arguments failed validation");
243
+ boundedJson(result.value, output ? LIMITS.outputBytes : LIMITS.inputBytes);
244
+ return result.value;
245
+ }
246
+
247
+ // packages/core/bootstrap/app.ts
248
+ var app_default = defineApp({ schema: {}, functions: {} });
249
+
250
+ // packages/flare/dist/sdk/cloudflare.js
251
+ import { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
252
+
253
+ // packages/flare/dist/sdk/backup.js
254
+ var SNAPSHOT_BYTES = 4 * 1048576;
255
+ var SNAPSHOT_ROWS = 1e4;
256
+ var reject = (code) => {
257
+ throw new FlareError(code, code);
258
+ };
259
+ function emptyData(storage, schema) {
260
+ for (const name of Object.keys(schema))
261
+ if (storage.sql.exec(`SELECT 1 FROM ${quote(name)} LIMIT 1`).toArray().length)
262
+ return false;
263
+ return !storage.sql.exec("SELECT 1 FROM _flared_outbox LIMIT 1").toArray().length;
264
+ }
265
+ function exportData(storage, schema) {
266
+ if (storage.sql.exec("SELECT 1 FROM _flared_outbox LIMIT 1").toArray().length)
267
+ reject("BACKUP_BUSY");
268
+ const snapshot = {
269
+ format: 1,
270
+ schema: schemaContract(schema),
271
+ revision: Number(storage.sql.exec("SELECT value FROM _flared_revision WHERE id=1").one().value),
272
+ tables: Object.create(null)
273
+ };
274
+ let count = 0, bytes = 0;
275
+ for (const name of Object.keys(schema)) {
276
+ const docs = [];
277
+ snapshot.tables[name] = docs;
278
+ let cursor = "";
279
+ for (;; ) {
280
+ const page = storage.sql.exec(`SELECT * FROM ${quote(name)} WHERE _id>? ORDER BY _id LIMIT 100`, cursor).toArray();
281
+ for (const row of page) {
282
+ const doc = {
283
+ _id: String(row._id),
284
+ _creationTime: Number(row._creationTime),
285
+ value: JSON.parse(String(row._data))
286
+ };
287
+ bytes += new TextEncoder().encode(stableJson(doc)).length;
288
+ if (++count > SNAPSHOT_ROWS || bytes > SNAPSHOT_BYTES)
289
+ reject("BACKUP_TOO_LARGE");
290
+ docs.push(doc);
291
+ }
292
+ if (page.length < 100)
293
+ break;
294
+ cursor = String(page.at(-1)._id);
295
+ }
296
+ }
297
+ return JSON.parse(boundedJson(snapshot, SNAPSHOT_BYTES));
298
+ }
299
+ async function restoreData(storage, schema, id, snapshot) {
300
+ boundedJson(snapshot, SNAPSHOT_BYTES);
301
+ if (!/^[0-9a-f-]{36}$/.test(id) || snapshot?.format !== 1 || !Number.isSafeInteger(snapshot.revision) || snapshot.revision < 0 || stableJson(snapshot.schema) !== stableJson(schemaContract(schema)) || !snapshot.tables || Array.isArray(snapshot.tables) || Object.keys(snapshot.tables).sort().join() !== Object.keys(schema).sort().join())
302
+ reject("INVALID_BACKUP");
303
+ await storage.transaction(async () => {
304
+ storage.sql.exec("CREATE TABLE IF NOT EXISTS _flared_restore (id TEXT PRIMARY KEY)");
305
+ if (storage.sql.exec("SELECT id FROM _flared_restore WHERE id=?", id).toArray().length)
306
+ return;
307
+ if (!emptyData(storage, schema))
308
+ reject("RESTORE_NOT_EMPTY");
309
+ let count = 0;
310
+ for (const [name, docs] of Object.entries(snapshot.tables)) {
311
+ if (!Array.isArray(docs))
312
+ reject("INVALID_BACKUP");
313
+ for (const doc of docs) {
314
+ if (++count > SNAPSHOT_ROWS || !doc || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(doc._id) || !Number.isFinite(doc._creationTime) || doc._creationTime < 0)
315
+ reject("INVALID_BACKUP");
316
+ const value = await documentValue(schema[name], doc.value);
317
+ if (stableJson(value) !== stableJson(doc.value))
318
+ reject("INVALID_BACKUP");
319
+ storage.sql.exec(`INSERT INTO ${quote(name)} VALUES (?,?,?)`, doc._id, doc._creationTime, stableJson(value));
320
+ }
321
+ }
322
+ storage.sql.exec("UPDATE _flared_revision SET value=? WHERE id=1", snapshot.revision);
323
+ storage.sql.exec("DELETE FROM _flared_changes");
324
+ for (const name of Object.keys(schema))
325
+ storage.sql.exec("INSERT INTO _flared_changes VALUES (?,?)", name, snapshot.revision);
326
+ storage.sql.exec("INSERT INTO _flared_restore VALUES (?)", id);
327
+ });
328
+ await storage.sync();
329
+ return { restored: true, revision: snapshot.revision };
330
+ }
331
+
332
+ // packages/flare/dist/sdk/database.js
333
+ var scalar = (v) => v === null || typeof v === "string" || typeof v === "boolean" || typeof v === "number" && Number.isFinite(v);
334
+ var bind = (v) => typeof v === "boolean" ? Number(v) : v;
335
+ var checkedId = (id) => {
336
+ if (typeof id !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id))
337
+ throw new FlareError("INVALID_INPUT", "Invalid document ID");
338
+ };
339
+ var size = (limit) => {
340
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100)
341
+ throw new FlareError("INVALID_INPUT", "Page size must be 1..100");
342
+ };
343
+ function database(sql, schema, writable, touched = new Set, changed = new Set) {
344
+ let active = true;
345
+ const pending = new Set;
346
+ let operations = 0;
347
+ const check = () => {
348
+ if (!active)
349
+ throw new FlareError("CAPABILITY_EXPIRED", "Database invocation ended");
350
+ };
351
+ const writing = () => {
352
+ check();
353
+ if (!writable)
354
+ throw new FlareError("READ_ONLY", "Queries cannot write");
355
+ };
356
+ function table(name) {
357
+ check();
358
+ if (!Object.hasOwn(schema, name))
359
+ throw new FlareError("INVALID_TABLE", "Unknown table");
360
+ touched.add(name);
361
+ return schema[name];
362
+ }
363
+ const task = (fn, count = true) => {
364
+ if (count && ++operations > 1000) {
365
+ const rejected = Promise.reject(new FlareError("LIMIT_EXCEEDED", "Maximum 1000 database operations per invocation"));
366
+ rejected.catch(() => {
367
+ return;
368
+ });
369
+ pending.add(rejected);
370
+ return rejected;
371
+ }
372
+ const promise = fn();
373
+ pending.add(promise);
374
+ promise.then(() => pending.delete(promise), () => {
375
+ return;
376
+ });
377
+ return promise;
378
+ };
379
+ const decode = (row) => ({
380
+ ...JSON.parse(String(row._data)),
381
+ _id: String(row._id),
382
+ _creationTime: Number(row._creationTime)
383
+ });
384
+ const get = (name, id) => task(async () => {
385
+ table(name);
386
+ checkedId(id);
387
+ const row = sql.exec(`SELECT * FROM ${quote(name)} WHERE _id=?`, id).toArray()[0];
388
+ return row ? decode(row) : null;
389
+ });
390
+ function query(name) {
391
+ const definition = table(name);
392
+ function make(index, filters, direction) {
393
+ const page = (options) => task(async () => {
394
+ table(name);
395
+ size(options.limit);
396
+ const fields = [
397
+ ...index ? definition.indexes[index] : [],
398
+ "_creationTime",
399
+ "_id"
400
+ ];
401
+ const expressions = fields.map(fieldSql);
402
+ const signature = stableJson({ name, index, filters, direction });
403
+ const where = filters.map((f) => `${fieldSql(f.field)} ${f.operator === "=" ? "IS" : f.operator} ?`);
404
+ const bindings = filters.map((f) => bind(f.value));
405
+ if (options.cursor != null) {
406
+ let token;
407
+ try {
408
+ if (options.cursor.length > 8192)
409
+ throw Error();
410
+ token = JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(options.cursor), (c) => c.charCodeAt(0))));
411
+ } catch {
412
+ throw new FlareError("INVALID_CURSOR", "Invalid cursor");
413
+ }
414
+ if (token.signature !== signature || !Array.isArray(token.values) || token.values.length !== fields.length || !token.values.every(scalar))
415
+ throw new FlareError("INVALID_CURSOR", "Cursor belongs to a different query");
416
+ const terms = expressions.map((expr, i) => {
417
+ const prefix = expressions.slice(0, i).map((e) => `${e} IS ?`);
418
+ bindings.push(...token.values.slice(0, i).map(bind));
419
+ const value = token.values[i];
420
+ if (value === null)
421
+ prefix.push(direction === "asc" ? `${expr} IS NOT NULL` : "0");
422
+ else {
423
+ prefix.push(direction === "asc" ? `${expr} > ?` : `(${expr} < ? OR ${expr} IS NULL)`);
424
+ bindings.push(bind(value));
425
+ }
426
+ return "(" + prefix.join(" AND ") + ")";
427
+ });
428
+ where.push("(" + terms.join(" OR ") + ")");
429
+ }
430
+ const rows = sql.exec(`SELECT *,${expressions.map((e, i) => `${e} AS __k${i}`).join(",")} FROM ${quote(name)} ${where.length ? "WHERE " + where.join(" AND ") : ""} ORDER BY ${expressions.map((e) => `${e} ${direction}`).join(",")} LIMIT ?`, ...bindings, options.limit + 1).toArray();
431
+ let more = rows.length > options.limit;
432
+ if (more)
433
+ rows.pop();
434
+ const docs = [];
435
+ let bytes = 2;
436
+ for (const row of rows) {
437
+ const doc = decode(row), length = new TextEncoder().encode(JSON.stringify(doc)).length + (docs.length ? 1 : 0);
438
+ if (bytes + length > 262144) {
439
+ more = true;
440
+ break;
441
+ }
442
+ docs.push(doc);
443
+ bytes += length;
444
+ }
445
+ const last = rows[docs.length - 1];
446
+ const continueCursor = more && last ? btoa(String.fromCharCode(...new TextEncoder().encode(boundedJson({
447
+ signature,
448
+ values: expressions.map((_, i) => last[`__k${i}`])
449
+ }, 6144)))) : null;
450
+ boundedJson(docs, 262144);
451
+ return { page: docs, continueCursor, isDone: !more };
452
+ });
453
+ return Object.freeze({
454
+ withIndex(next, range) {
455
+ check();
456
+ if (index || !Object.hasOwn(definition.indexes, next))
457
+ throw new FlareError("INVALID_INDEX", "Unknown or duplicate index");
458
+ const selected = [], fields = definition.indexes[next];
459
+ let pos = 0, open = true;
460
+ const bounds = new Set;
461
+ const add = (operator) => (field, value) => {
462
+ if (!open)
463
+ throw new FlareError("CAPABILITY_EXPIRED", "Index range declaration ended");
464
+ if (!scalar(value) || operator !== "=" && value === null || !fields.includes(field) || field !== fields[pos])
465
+ throw new FlareError("INVALID_INPUT", "Index range must follow its field order");
466
+ const bound = operator === "=" ? "equal" : operator.startsWith(">") ? "lower" : "upper";
467
+ if (bound === "equal" && bounds.size || bounds.has(bound))
468
+ throw new FlareError("INVALID_INPUT", "Duplicate or mixed index bounds");
469
+ selected.push({ field, operator, value });
470
+ if (operator === "=") {
471
+ pos++;
472
+ bounds.clear();
473
+ } else
474
+ bounds.add(bound);
475
+ if (selected.length > fields.length + 2)
476
+ throw new FlareError("INVALID_INPUT", "Index range exceeds limits");
477
+ return q;
478
+ };
479
+ const q = Object.freeze({
480
+ eq: add("="),
481
+ gt: add(">"),
482
+ gte: add(">="),
483
+ lt: add("<"),
484
+ lte: add("<=")
485
+ });
486
+ try {
487
+ if (range && range(q) !== q)
488
+ throw new FlareError("INVALID_INPUT", "Invalid index range");
489
+ } finally {
490
+ open = false;
491
+ }
492
+ return make(next, Object.freeze([...selected]), direction);
493
+ },
494
+ order(next) {
495
+ check();
496
+ if (!["asc", "desc"].includes(next))
497
+ throw new FlareError("INVALID_INPUT", "Invalid order");
498
+ return make(index, filters, next);
499
+ },
500
+ paginate: page,
501
+ take(limit) {
502
+ return task(async () => {
503
+ const result = await page({ limit });
504
+ if (!result.isDone && result.page.length < limit)
505
+ throw new FlareError("TOO_LARGE", "Use paginate for large documents");
506
+ return result.page;
507
+ }, false);
508
+ },
509
+ first() {
510
+ return task(async () => (await page({ limit: 1 })).page[0] ?? null, false);
511
+ }
512
+ });
513
+ }
514
+ return make(null, [], "asc");
515
+ }
516
+ const read = { get, query };
517
+ const write = {
518
+ insert(name, input) {
519
+ return task(async () => {
520
+ writing();
521
+ const definition = table(name), value = await documentValue(definition, input);
522
+ writing();
523
+ const id = crypto.randomUUID();
524
+ sql.exec(`INSERT INTO ${quote(name)} VALUES (?,?,?)`, id, Date.now(), stableJson(value));
525
+ changed.add(name);
526
+ return id;
527
+ });
528
+ },
529
+ patch(name, id, patch) {
530
+ return task(async () => {
531
+ writing();
532
+ const definition = table(name);
533
+ checkedId(id);
534
+ boundedJson(patch, 65536);
535
+ if (!patch || Array.isArray(patch) || typeof patch !== "object")
536
+ throw new FlareError("INVALID_INPUT", "Invalid patch");
537
+ const row = sql.exec(`SELECT _data FROM ${quote(name)} WHERE _id=?`, id).toArray()[0];
538
+ if (!row)
539
+ throw new FlareError("NOT_FOUND", "Document not found");
540
+ const value = await documentValue(definition, {
541
+ ...JSON.parse(String(row._data)),
542
+ ...patch
543
+ });
544
+ writing();
545
+ sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=?`, stableJson(value), id);
546
+ changed.add(name);
547
+ });
548
+ },
549
+ replace(name, id, input) {
550
+ return task(async () => {
551
+ writing();
552
+ const definition = table(name);
553
+ checkedId(id);
554
+ const value = await documentValue(definition, input);
555
+ writing();
556
+ const rows = sql.exec(`UPDATE ${quote(name)} SET _data=? WHERE _id=? RETURNING _id`, stableJson(value), id).toArray();
557
+ if (!rows.length)
558
+ throw new FlareError("NOT_FOUND", "Document not found");
559
+ changed.add(name);
560
+ });
561
+ },
562
+ delete(name, id) {
563
+ return task(async () => {
564
+ writing();
565
+ table(name);
566
+ checkedId(id);
567
+ if (!sql.exec(`DELETE FROM ${quote(name)} WHERE _id=? RETURNING _id`, id).toArray().length)
568
+ throw new FlareError("NOT_FOUND", "Document not found");
569
+ changed.add(name);
570
+ });
571
+ }
572
+ };
573
+ return {
574
+ db: Object.freeze(writable ? { ...read, ...write } : read),
575
+ async finish() {
576
+ try {
577
+ while (pending.size) {
578
+ const settled = await Promise.allSettled([...pending]);
579
+ const failed = settled.find((result) => result.status === "rejected");
580
+ if (failed?.status === "rejected")
581
+ throw failed.reason;
582
+ }
583
+ } finally {
584
+ active = false;
585
+ }
586
+ },
587
+ revoke() {
588
+ active = false;
589
+ }
590
+ };
591
+ }
592
+
593
+ // packages/flare/dist/sdk/cloudflare.js
594
+ function authorize(mode, auth, internal = false) {
595
+ if (mode === "internal" && !internal)
596
+ throw new FlareError("UNAUTHORIZED", "Internal function");
597
+ if (auth && (!auth.subject || !auth.issuer || !Number.isFinite(auth.expiresAt) || auth.expiresAt <= Date.now()))
598
+ throw new FlareError("AUTH_EXPIRED", "Authorization expired");
599
+ if (mode === "required" && !auth)
600
+ throw new FlareError("UNAUTHORIZED", "Authentication required");
601
+ }
602
+ function createFacet(app) {
603
+ const functions = flatten(app.functions);
604
+ const contract = schemaContract(app.schema);
605
+ return class AppFacet extends DurableObject {
606
+ tail = Promise.resolve();
607
+ constructor(ctx, env) {
608
+ super(ctx, env);
609
+ ctx.blockConcurrencyWhile(async () => {
610
+ await applySchema(ctx.storage, app.schema);
611
+ ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS _flared_revision (id INTEGER PRIMARY KEY CHECK(id=1), value INTEGER NOT NULL)");
612
+ ctx.storage.sql.exec("INSERT OR IGNORE INTO _flared_revision VALUES (1,0)");
613
+ ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS _flared_outbox (id TEXT PRIMARY KEY, release TEXT NOT NULL, path TEXT NOT NULL, args TEXT NOT NULL, auth TEXT NOT NULL, dueAt INTEGER NOT NULL,internal INTEGER NOT NULL DEFAULT 0)");
614
+ ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS _flared_changes (name TEXT PRIMARY KEY,revision INTEGER NOT NULL)");
615
+ ctx.storage.sql.exec("CREATE INDEX IF NOT EXISTS outbox_pending ON _flared_outbox(dueAt,id)");
616
+ });
617
+ }
618
+ serial(fn) {
619
+ const result = this.tail.then(fn);
620
+ this.tail = result.catch(() => {
621
+ return;
622
+ });
623
+ return result;
624
+ }
625
+ revision() {
626
+ return Number(this.ctx.storage.sql.exec("SELECT value FROM _flared_revision WHERE id=1").one().value);
627
+ }
628
+ snapshot() {
629
+ return this.serial(async () => ({
630
+ revision: this.revision(),
631
+ changes: Object.fromEntries(this.ctx.storage.sql.exec("SELECT * FROM _flared_changes").toArray().map((row) => [String(row.name), Number(row.revision)]))
632
+ }));
633
+ }
634
+ async invoke(call) {
635
+ try {
636
+ return { ok: true, value: await this.execute(call) };
637
+ } catch (error) {
638
+ return {
639
+ ok: false,
640
+ code: errorCode(error)
641
+ };
642
+ }
643
+ }
644
+ async administrative(operation, input) {
645
+ try {
646
+ const work = {
647
+ inspect: () => this.inspect(input),
648
+ edit: () => this.edit(input),
649
+ "backup-export": () => this.serial(async () => exportData(this.ctx.storage, app.schema)),
650
+ "backup-empty": () => this.serial(async () => emptyData(this.ctx.storage, app.schema)),
651
+ "backup-restore": () => this.serial(() => restoreData(this.ctx.storage, app.schema, input.id, input.snapshot))
652
+ };
653
+ if (!Object.hasOwn(work, operation))
654
+ throw new FlareError("INVALID_OPERATION", "Unknown administrative capability");
655
+ return { ok: true, value: await work[operation]() };
656
+ } catch (error) {
657
+ return {
658
+ ok: false,
659
+ code: errorCode(error)
660
+ };
661
+ }
662
+ }
663
+ execute(call) {
664
+ return this.serial(async () => {
665
+ const fn = functions[call.path];
666
+ if (!fn || fn.kind !== call.kind || call.kind === "action")
667
+ throw new FlareError("FUNCTION_NOT_FOUND", "Data function not found");
668
+ authorize(fn.auth, call.auth, call.internal);
669
+ const args = await validate(fn.input, call.args);
670
+ if (call.kind === "query") {
671
+ return this.ctx.storage.transaction(async () => {
672
+ const tables = new Set;
673
+ const capability = database(this.ctx.storage.sql, app.schema, false, tables);
674
+ try {
675
+ const result = await validate(fn.output, await fn.handler({ db: capability.db, auth: call.auth }, args), true);
676
+ await capability.finish();
677
+ return { result, revision: this.revision(), tables: [...tables] };
678
+ } finally {
679
+ capability.revoke();
680
+ }
681
+ });
682
+ }
683
+ let result = null;
684
+ const changed = new Set;
685
+ await this.ctx.storage.transaction(async () => {
686
+ let active = true;
687
+ const scheduled = [];
688
+ const jobs = Object.freeze({
689
+ cancel: (id) => {
690
+ const pending = (async () => {
691
+ if (!active)
692
+ throw new FlareError("CAPABILITY_EXPIRED", "Mutation invocation ended");
693
+ if (scheduled.length >= 100)
694
+ throw new FlareError("LIMIT_EXCEEDED", "Maximum 100 job operations per mutation");
695
+ return this.cancelPending(id);
696
+ })();
697
+ scheduled.push(pending);
698
+ pending.catch(() => {
699
+ return;
700
+ });
701
+ return pending;
702
+ },
703
+ schedule: (path, jobArgs, options = {}) => {
704
+ const pending = (async () => {
705
+ if (!active)
706
+ throw new FlareError("CAPABILITY_EXPIRED", "Mutation invocation ended");
707
+ if (scheduled.length >= 100)
708
+ throw new FlareError("LIMIT_EXCEEDED", "Maximum 100 jobs per mutation");
709
+ const target = functions[path];
710
+ if (!target || target.kind !== "action")
711
+ throw new FlareError("FUNCTION_NOT_FOUND", "Scheduled action not found");
712
+ const delay = options.delaySeconds ?? 0;
713
+ if (!Number.isFinite(delay) || delay < 0 || delay > 31536000)
714
+ throw new FlareError("INVALID_INPUT", "Invalid job delay");
715
+ const input = await validate(target.input, jobArgs);
716
+ if (!active)
717
+ throw new FlareError("CAPABILITY_EXPIRED", "Mutation invocation ended");
718
+ const id = crypto.randomUUID();
719
+ this.ctx.storage.sql.exec("INSERT INTO _flared_outbox (id,release,path,args,auth,dueAt) VALUES (?,?,?,?,?,?)", id, this.env.RELEASE_ID, path, JSON.stringify(input), JSON.stringify(call.auth), Date.now() + Math.ceil(delay * 1000));
720
+ if (target.auth === "internal")
721
+ this.ctx.storage.sql.exec("UPDATE _flared_outbox SET internal=1,auth='null' WHERE id=?", id);
722
+ return id;
723
+ })();
724
+ scheduled.push(pending);
725
+ pending.catch(() => {
726
+ return;
727
+ });
728
+ return pending;
729
+ }
730
+ });
731
+ const capability = database(this.ctx.storage.sql, app.schema, true, new Set, changed);
732
+ try {
733
+ result = await validate(fn.output, await fn.handler({ db: capability.db, auth: call.auth, jobs }, args), true);
734
+ await capability.finish();
735
+ const settled = await Promise.allSettled(scheduled);
736
+ const failed = settled.find((result) => result.status === "rejected");
737
+ if (failed?.status === "rejected")
738
+ throw failed.reason;
739
+ } finally {
740
+ active = false;
741
+ capability.revoke();
742
+ await Promise.allSettled(scheduled);
743
+ }
744
+ this.commitRevision(changed);
745
+ });
746
+ await this.ctx.storage.sync();
747
+ return { result, revision: this.revision(), tables: [...changed] };
748
+ });
749
+ }
750
+ commitRevision(changed) {
751
+ this.ctx.storage.sql.exec("UPDATE _flared_revision SET value=value+1 WHERE id=1");
752
+ for (const name of changed)
753
+ this.ctx.storage.sql.exec("INSERT INTO _flared_changes VALUES (?,?) ON CONFLICT(name) DO UPDATE SET revision=excluded.revision", name, this.revision());
754
+ }
755
+ pendingJobs(limit = 100) {
756
+ return this.serial(async () => {
757
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100)
758
+ throw new FlareError("INVALID_INPUT", "Invalid outbox page size");
759
+ return this.ctx.storage.sql.exec("SELECT * FROM _flared_outbox ORDER BY dueAt,id LIMIT ?", limit).toArray().map((row) => ({
760
+ id: String(row.id),
761
+ release: String(row.release),
762
+ path: String(row.path),
763
+ args: JSON.parse(String(row.args)),
764
+ auth: JSON.parse(String(row.auth)),
765
+ dueAt: Number(row.dueAt),
766
+ internal: Number(row.internal) === 1
767
+ }));
768
+ });
769
+ }
770
+ cancelPending(id) {
771
+ if (typeof id !== "string" || !/^[a-z0-9_-]{1,128}$/.test(id))
772
+ throw new FlareError("INVALID_INPUT", "Invalid job ID");
773
+ return this.ctx.storage.sql.exec("DELETE FROM _flared_outbox WHERE id=? RETURNING id", id).toArray().length === 1;
774
+ }
775
+ cancelJob(id) {
776
+ return this.serial(async () => {
777
+ const cancelled = this.cancelPending(id);
778
+ await this.ctx.storage.sync();
779
+ return cancelled;
780
+ });
781
+ }
782
+ pendingJobReferences() {
783
+ return this.serial(async () => {
784
+ const paths = this.ctx.storage.sql.exec("SELECT DISTINCT path FROM _flared_outbox ORDER BY path LIMIT 201").toArray().map((row) => String(row.path));
785
+ const releases = this.ctx.storage.sql.exec("SELECT DISTINCT release FROM _flared_outbox ORDER BY release LIMIT 1001").toArray().map((row) => String(row.release));
786
+ return {
787
+ paths: paths.slice(0, 200),
788
+ releases: releases.slice(0, 1000),
789
+ truncated: paths.length > 200 || releases.length > 1000
790
+ };
791
+ });
792
+ }
793
+ validateJob(path, args) {
794
+ return this.serial(async () => {
795
+ const target = functions[path];
796
+ if (!target || target.kind !== "action")
797
+ throw new FlareError("FUNCTION_NOT_FOUND", "Scheduled action not found");
798
+ await validate(target.input, args);
799
+ });
800
+ }
801
+ enqueueJob(job) {
802
+ return this.serial(async () => {
803
+ const target = functions[job.path];
804
+ if (!target || target.kind !== "action")
805
+ throw new FlareError("FUNCTION_NOT_FOUND", "Scheduled action not found");
806
+ const args = await validate(target.input, job.args);
807
+ this.ctx.storage.sql.exec("INSERT OR IGNORE INTO _flared_outbox (id,release,path,args,auth,dueAt,internal) VALUES (?,?,?,?,?,?,1)", job.id, this.env.RELEASE_ID, job.path, JSON.stringify(args), "null", job.dueAt);
808
+ await this.ctx.storage.sync();
809
+ });
810
+ }
811
+ markJobsSent(ids) {
812
+ return this.serial(async () => {
813
+ if (!Array.isArray(ids) || ids.length > 100 || ids.some((id) => typeof id !== "string" || !/^[a-z0-9_-]{1,128}$/.test(id)))
814
+ throw new FlareError("INVALID_INPUT", "Invalid outbox IDs");
815
+ if (ids.length)
816
+ this.ctx.storage.transactionSync(() => {
817
+ this.ctx.storage.sql.exec(`DELETE FROM _flared_outbox WHERE id IN (${ids.map(() => "?").join(",")})`, ...ids);
818
+ });
819
+ await this.ctx.storage.sync();
820
+ });
821
+ }
822
+ inspect(input = {}) {
823
+ return this.serial(async () => {
824
+ if (!input.table)
825
+ return {
826
+ revision: this.revision(),
827
+ tables: Object.entries(contract).map(([name, definition]) => ({
828
+ name,
829
+ document: definition.document,
830
+ indexes: definition.indexes,
831
+ columns: this.columns(name)
832
+ }))
833
+ };
834
+ const capability = database(this.ctx.storage.sql, app.schema, false);
835
+ try {
836
+ const result = await capability.db.query(input.table).paginate({ limit: input.limit ?? 50, cursor: input.cursor });
837
+ await capability.finish();
838
+ return {
839
+ revision: this.revision(),
840
+ columns: this.columns(input.table),
841
+ rows: result.page,
842
+ next: result.continueCursor
843
+ };
844
+ } finally {
845
+ capability.revoke();
846
+ }
847
+ });
848
+ }
849
+ columns(name) {
850
+ const definition = contract[name];
851
+ if (!definition)
852
+ throw new FlareError("INVALID_TABLE", "Unknown table");
853
+ return [
854
+ { name: "_id", type: "TEXT", pk: 1, notnull: 1, dflt_value: null },
855
+ {
856
+ name: "_creationTime",
857
+ type: "REAL",
858
+ pk: 0,
859
+ notnull: 1,
860
+ dflt_value: null,
861
+ readonly: true
862
+ },
863
+ ...Object.entries(definition.document.properties).map(([field, value]) => ({
864
+ name: field,
865
+ type: value.type === "boolean" ? "BOOLEAN" : ["integer", "number"].includes(value.type) ? "REAL" : value.type === "string" ? "TEXT" : "JSON",
866
+ pk: 0,
867
+ notnull: Number((definition.document.required ?? []).includes(field)),
868
+ dflt_value: value.default === undefined ? null : JSON.stringify(value.default),
869
+ schema: value
870
+ }))
871
+ ];
872
+ }
873
+ edit(input) {
874
+ return this.serial(async () => {
875
+ boundedJson(input, LIMITS.inputBytes);
876
+ let rows = [];
877
+ const changed = new Set;
878
+ await this.ctx.storage.transaction(async () => {
879
+ if (input.expectedRevision !== this.revision())
880
+ throw new FlareError("CONFLICT", "Data changed; reload before editing");
881
+ const capability = database(this.ctx.storage.sql, app.schema, true, new Set, changed);
882
+ try {
883
+ if (input.operation === "insert") {
884
+ const id = await capability.db.insert(input.table, input.values ?? {});
885
+ rows = [await capability.db.get(input.table, id)];
886
+ } else if (input.key && input.operation === "update") {
887
+ await capability.db.patch(input.table, input.key, input.values ?? {});
888
+ rows = [await capability.db.get(input.table, input.key)];
889
+ } else if (input.key && input.operation === "delete") {
890
+ const row = await capability.db.get(input.table, input.key);
891
+ await capability.db.delete(input.table, input.key);
892
+ rows = [row];
893
+ } else
894
+ throw new FlareError("INVALID_INPUT", "Invalid edit");
895
+ await capability.finish();
896
+ boundedJson(rows, LIMITS.outputBytes);
897
+ } finally {
898
+ capability.revoke();
899
+ }
900
+ this.commitRevision(changed);
901
+ });
902
+ await this.ctx.storage.sync();
903
+ return { rows, revision: this.revision() };
904
+ });
905
+ }
906
+ };
907
+ }
908
+
909
+ // packages/core/bootstrap/facet-entry.ts
910
+ class AppFacet extends createFacet(app_default) {
911
+ }
912
+ export {
913
+ AppFacet
914
+ };