bleam 0.0.11 → 0.0.12
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/dist/ai.cjs +2314 -2173
- package/dist/ai.d.cts +227 -322
- package/dist/ai.d.ts +227 -322
- package/dist/ai.js +2309 -2159
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/config.d.cts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/{files-Ds1wT8C2.js → files-BXVkPrPN.js} +6 -1
- package/dist/{files-Bo7h9fik.cjs → files-DxaQ-Nv0.cjs} +11 -0
- package/dist/files.cjs +1 -1
- package/dist/files.d.cts +1 -1
- package/dist/files.d.ts +1 -1
- package/dist/files.js +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/schema-B5BfdswF.js +226 -0
- package/dist/schema-BnVZOXfu.cjs +286 -0
- package/dist/schema-D5eImHxu.d.cts +125 -0
- package/dist/schema-SSjokbtw.d.ts +125 -0
- package/dist/schema.cjs +10 -2
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +2 -2
- package/dist/state-Dh3HLixb.js +874 -0
- package/dist/state-LssDgpff.cjs +973 -0
- package/dist/state.cjs +16 -12
- package/dist/state.d.cts +144 -140
- package/dist/state.d.ts +145 -140
- package/dist/state.js +3 -3
- package/dist/{ui-CHc4xEs_.d.ts → ui-D7bRLYee.d.ts} +7 -7
- package/dist/ui.d.ts +1 -1
- package/dist/window.d.ts +1 -1
- package/package.json +3 -1
- package/templates/foundation-models/app/index.tsx +77 -15
- package/templates/image-generation/app/index.tsx +2 -2
- package/templates/native/ios/Bleam.xcodeproj/project.pbxproj +46 -46
- package/templates/native/ios/Podfile.lock +173 -173
- package/templates/native/modules/bleam-runtime/ios/AIModule.swift +35 -357
- package/templates/text-generation/app/index.tsx +81 -50
- package/templates/updates/README.md +1 -1
- package/dist/schema-Bo5Jvqus.js +0 -90
- package/dist/schema-CYh6n8GS.d.ts +0 -58
- package/dist/schema-oeOrd3i1.d.cts +0 -58
- package/dist/schema-rQ13mrpD.cjs +0 -102
- package/dist/state-Bx0VlTlO.cjs +0 -852
- package/dist/state-CAwe-Vw1.js +0 -767
- /package/dist/{config-Cms0rvqg.d.ts → config-COcRnn5a.d.cts} +0 -0
- /package/dist/{config-CufOVJV3.d.cts → config-Chi-flpJ.d.ts} +0 -0
- /package/dist/{files-4ZEoAWiv.d.ts → files-DwA7pzr3.d.cts} +0 -0
- /package/dist/{files-Dt5mbzLq.d.cts → files-VrkQlKIT.d.ts} +0 -0
package/dist/state-CAwe-Vw1.js
DELETED
|
@@ -1,767 +0,0 @@
|
|
|
1
|
-
import { t as ensureNativeScript } from "./native-runtime-C85Nuc4F.js";
|
|
2
|
-
import { t as crypto } from "./crypto-BB92-Upx.js";
|
|
3
|
-
import { n as schema } from "./schema-Bo5Jvqus.js";
|
|
4
|
-
import { use, useSyncExternalStore } from "react";
|
|
5
|
-
|
|
6
|
-
//#region src/state/atom.ts
|
|
7
|
-
var LiveStore = class {
|
|
8
|
-
listeners = /* @__PURE__ */ new Set();
|
|
9
|
-
constructor(value) {
|
|
10
|
-
this.value = value;
|
|
11
|
-
}
|
|
12
|
-
get() {
|
|
13
|
-
return this.value;
|
|
14
|
-
}
|
|
15
|
-
set(value) {
|
|
16
|
-
if (Object.is(this.value, value)) return;
|
|
17
|
-
this.value = value;
|
|
18
|
-
for (const listener of this.listeners) listener();
|
|
19
|
-
}
|
|
20
|
-
update(updater) {
|
|
21
|
-
this.set(updater(this.value));
|
|
22
|
-
}
|
|
23
|
-
subscribe(listener) {
|
|
24
|
-
this.listeners.add(listener);
|
|
25
|
-
return () => {
|
|
26
|
-
this.listeners.delete(listener);
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
function atom(initialValue) {
|
|
31
|
-
return new LiveStore(initialValue);
|
|
32
|
-
}
|
|
33
|
-
function useStore(store) {
|
|
34
|
-
return useSyncExternalStore(store.subscribe.bind(store), store.get.bind(store), store.get.bind(store));
|
|
35
|
-
}
|
|
36
|
-
function useAtom(value) {
|
|
37
|
-
const current = useStore(value);
|
|
38
|
-
const set = (next) => {
|
|
39
|
-
if (typeof next === "function") {
|
|
40
|
-
value.update(next);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
value.set(next);
|
|
44
|
-
};
|
|
45
|
-
return [current, set];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
//#endregion
|
|
49
|
-
//#region src/app-storage.ts
|
|
50
|
-
let cachedStorageKey;
|
|
51
|
-
function launchArgument(name) {
|
|
52
|
-
const argumentsList = NSProcessInfo.processInfo.arguments();
|
|
53
|
-
const count = Number(argumentsList.count ?? 0);
|
|
54
|
-
for (let index = 0; index < count - 1; index += 1) if (String(argumentsList.objectAtIndex(index)) === name) return String(argumentsList.objectAtIndex(index + 1));
|
|
55
|
-
}
|
|
56
|
-
function storageKey(name) {
|
|
57
|
-
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "app";
|
|
58
|
-
}
|
|
59
|
-
function currentAppStorageKey() {
|
|
60
|
-
if (cachedStorageKey) return cachedStorageKey;
|
|
61
|
-
ensureNativeScript();
|
|
62
|
-
const configPath = launchArgument("--bleam-project-config");
|
|
63
|
-
if (configPath) try {
|
|
64
|
-
const text = String(NSString.stringWithContentsOfFileEncodingError(configPath, NSUTF8StringEncoding, null));
|
|
65
|
-
const config = JSON.parse(text);
|
|
66
|
-
if (typeof config.id === "string" && config.id) {
|
|
67
|
-
cachedStorageKey = config.id;
|
|
68
|
-
return cachedStorageKey;
|
|
69
|
-
}
|
|
70
|
-
if (typeof config.name === "string" && config.name) {
|
|
71
|
-
cachedStorageKey = storageKey(config.name);
|
|
72
|
-
return cachedStorageKey;
|
|
73
|
-
}
|
|
74
|
-
} catch {}
|
|
75
|
-
cachedStorageKey = "app";
|
|
76
|
-
return cachedStorageKey;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
//#endregion
|
|
80
|
-
//#region src/state/collection.ts
|
|
81
|
-
const collectionsByName = /* @__PURE__ */ new Map();
|
|
82
|
-
let database;
|
|
83
|
-
let schemaInitialized;
|
|
84
|
-
function getDatabase() {
|
|
85
|
-
database ??= import("./native-sqlite-xcGdamRD.js").then((module) => module.openNativeSQLiteDatabase(`${currentAppStorageKey()}.sqlite3`));
|
|
86
|
-
return database;
|
|
87
|
-
}
|
|
88
|
-
async function initializeCollectionSchema() {
|
|
89
|
-
schemaInitialized ??= (async () => {
|
|
90
|
-
const database$1 = await getDatabase();
|
|
91
|
-
await database$1.execute("PRAGMA journal_mode = WAL");
|
|
92
|
-
await database$1.execute(`CREATE TABLE IF NOT EXISTS collections (
|
|
93
|
-
collection TEXT NOT NULL,
|
|
94
|
-
id TEXT NOT NULL,
|
|
95
|
-
data TEXT NOT NULL,
|
|
96
|
-
createdAt TEXT NOT NULL,
|
|
97
|
-
updatedAt TEXT NOT NULL,
|
|
98
|
-
version INTEGER NOT NULL DEFAULT 1,
|
|
99
|
-
PRIMARY KEY (collection, id)
|
|
100
|
-
)`);
|
|
101
|
-
await database$1.execute(`CREATE TABLE IF NOT EXISTS links (
|
|
102
|
-
id TEXT PRIMARY KEY,
|
|
103
|
-
collections TEXT NOT NULL,
|
|
104
|
-
ids TEXT NOT NULL,
|
|
105
|
-
createdAt TEXT NOT NULL
|
|
106
|
-
)`);
|
|
107
|
-
await database$1.execute("CREATE INDEX IF NOT EXISTS collections_collection_createdAt_idx ON collections(collection, createdAt)");
|
|
108
|
-
await database$1.execute("CREATE UNIQUE INDEX IF NOT EXISTS links_collections_ids_idx ON links(collections, ids)");
|
|
109
|
-
await database$1.execute("CREATE INDEX IF NOT EXISTS links_collections_idx ON links(collections)");
|
|
110
|
-
})();
|
|
111
|
-
return schemaInitialized;
|
|
112
|
-
}
|
|
113
|
-
async function initializedDatabase() {
|
|
114
|
-
await initializeCollectionSchema();
|
|
115
|
-
return getDatabase();
|
|
116
|
-
}
|
|
117
|
-
function createEntry() {
|
|
118
|
-
return {
|
|
119
|
-
value: void 0,
|
|
120
|
-
error: void 0,
|
|
121
|
-
promise: null,
|
|
122
|
-
version: 0,
|
|
123
|
-
listeners: /* @__PURE__ */ new Set()
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
function notify(entry) {
|
|
127
|
-
for (const listener of entry.listeners) listener();
|
|
128
|
-
}
|
|
129
|
-
function stableStringify(value) {
|
|
130
|
-
if (value === null || value === void 0) return "";
|
|
131
|
-
if (typeof value !== "object") return String(value);
|
|
132
|
-
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
133
|
-
return `{${Object.keys(value).sort().map((key) => `${key}:${stableStringify(value[key])}`).join("|")}}`;
|
|
134
|
-
}
|
|
135
|
-
function entryChanged(entry, nextValue) {
|
|
136
|
-
return stableStringify(entry.value) !== stableStringify(nextValue);
|
|
137
|
-
}
|
|
138
|
-
function rowValue(row, key) {
|
|
139
|
-
const value = row[key];
|
|
140
|
-
if (typeof value !== "string") throw new Error(`Expected SQLite row field ${key} to be a string`);
|
|
141
|
-
return value;
|
|
142
|
-
}
|
|
143
|
-
function parsePersistedRow(row) {
|
|
144
|
-
return {
|
|
145
|
-
id: rowValue(row, "id"),
|
|
146
|
-
data: rowValue(row, "data"),
|
|
147
|
-
createdAt: rowValue(row, "createdAt"),
|
|
148
|
-
updatedAt: rowValue(row, "updatedAt")
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
function decodeRecordData(collection$1, data) {
|
|
152
|
-
const value = JSON.parse(data);
|
|
153
|
-
return collection$1.validator.parse(value);
|
|
154
|
-
}
|
|
155
|
-
function encodeRecordData(collection$1, data) {
|
|
156
|
-
return JSON.stringify(collection$1.validator.parse(data));
|
|
157
|
-
}
|
|
158
|
-
function recordKey(collection$1, id) {
|
|
159
|
-
return stableStringify([
|
|
160
|
-
"view",
|
|
161
|
-
typeof collection$1 === "string" ? collection$1 : collection$1.name,
|
|
162
|
-
id
|
|
163
|
-
]);
|
|
164
|
-
}
|
|
165
|
-
function listKey(collection$1, options) {
|
|
166
|
-
return stableStringify([
|
|
167
|
-
"list",
|
|
168
|
-
collection$1.name,
|
|
169
|
-
normalizeListOptions(options)
|
|
170
|
-
]);
|
|
171
|
-
}
|
|
172
|
-
function normalizeWhereValue(value) {
|
|
173
|
-
if (Array.isArray(value)) {
|
|
174
|
-
if (value.length === 2 && typeof value[0] === "string") return [value];
|
|
175
|
-
return [...value].map(([operator, operand]) => [operator, operand]).sort((left, right) => stableStringify(left).localeCompare(stableStringify(right)));
|
|
176
|
-
}
|
|
177
|
-
return [["equals", value]];
|
|
178
|
-
}
|
|
179
|
-
function normalizeListOptions(options) {
|
|
180
|
-
if (!options) return {};
|
|
181
|
-
return {
|
|
182
|
-
where: options.where ? Object.fromEntries(Object.entries(options.where).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, normalizeWhereValue(value)])) : void 0,
|
|
183
|
-
sortBy: options.sortBy ? String(options.sortBy) : void 0,
|
|
184
|
-
order: options.order,
|
|
185
|
-
take: options.take
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
function compileListDependencies(collection$1, options) {
|
|
189
|
-
const paths = /* @__PURE__ */ new Set();
|
|
190
|
-
for (const key of Object.keys(options.where ?? {})) paths.add(key);
|
|
191
|
-
if (options.sortBy && options.sortBy !== "createdAt" && options.sortBy !== "updatedAt") paths.add(options.sortBy);
|
|
192
|
-
return {
|
|
193
|
-
collection: collection$1,
|
|
194
|
-
paths,
|
|
195
|
-
options
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
function getPathValue(record, path) {
|
|
199
|
-
if (!record) return;
|
|
200
|
-
if (path === "createdAt" || path === "updatedAt") return record[path];
|
|
201
|
-
return record.data[path];
|
|
202
|
-
}
|
|
203
|
-
function createRecordChange(collection$1, id, previous, next) {
|
|
204
|
-
const changedPaths = /* @__PURE__ */ new Set();
|
|
205
|
-
if (previous?.createdAt !== next?.createdAt) changedPaths.add("createdAt");
|
|
206
|
-
if (previous?.updatedAt !== next?.updatedAt) changedPaths.add("updatedAt");
|
|
207
|
-
const paths = new Set([...Object.keys(previous?.data ?? {}), ...Object.keys(next?.data ?? {})]);
|
|
208
|
-
for (const path of paths) if (stableStringify(getPathValue(previous, path)) !== stableStringify(getPathValue(next, path))) changedPaths.add(path);
|
|
209
|
-
return {
|
|
210
|
-
collection: collection$1,
|
|
211
|
-
id,
|
|
212
|
-
previous,
|
|
213
|
-
next,
|
|
214
|
-
changedPaths
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
function listMayBeAffectedByRecordChange(dependencies, change, currentIds) {
|
|
218
|
-
if (dependencies.collection !== change.collection) return false;
|
|
219
|
-
if (change.previous === null && change.next !== null) return true;
|
|
220
|
-
if (change.previous !== null && change.next === null) return currentIds ? currentIds.includes(change.id) : true;
|
|
221
|
-
if (dependencies.paths.size === 0) return false;
|
|
222
|
-
for (const path of change.changedPaths) if (dependencies.paths.has(path)) return true;
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
function encodePart(value) {
|
|
226
|
-
return encodeURIComponent(value);
|
|
227
|
-
}
|
|
228
|
-
function encodePair(first, second) {
|
|
229
|
-
return `${encodePart(first)}:${encodePart(second)}`;
|
|
230
|
-
}
|
|
231
|
-
function decodePair(value) {
|
|
232
|
-
const parts = value.split(":");
|
|
233
|
-
if (parts.length !== 2 || parts[0] === void 0 || parts[1] === void 0) throw new Error(`Invalid encoded link pair "${value}"`);
|
|
234
|
-
return [decodeURIComponent(parts[0]), decodeURIComponent(parts[1])];
|
|
235
|
-
}
|
|
236
|
-
function storedLinkFor(first, second) {
|
|
237
|
-
const sameCollection = first.collection.name === second.collection.name;
|
|
238
|
-
const ordered = first.collection.name < second.collection.name || sameCollection && first.id <= second.id ? [first, second] : [second, first];
|
|
239
|
-
const collections = encodePair(ordered[0].collection.name, ordered[1].collection.name);
|
|
240
|
-
const ids = encodePair(ordered[0].id, ordered[1].id);
|
|
241
|
-
return {
|
|
242
|
-
id: encodePair(collections, ids),
|
|
243
|
-
collections,
|
|
244
|
-
ids
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
function linkIncludesRecord(row, collectionName, id) {
|
|
248
|
-
const [firstCollection, secondCollection] = decodePair(rowValue(row, "collections"));
|
|
249
|
-
const [firstId, secondId] = decodePair(rowValue(row, "ids"));
|
|
250
|
-
return firstCollection === collectionName && firstId === id || secondCollection === collectionName && secondId === id;
|
|
251
|
-
}
|
|
252
|
-
function linkedIdFromRow(row, source, targetName) {
|
|
253
|
-
const [firstCollection, secondCollection] = decodePair(rowValue(row, "collections"));
|
|
254
|
-
const [firstId, secondId] = decodePair(rowValue(row, "ids"));
|
|
255
|
-
if (firstCollection === source.collection && firstId === source.id && secondCollection === targetName) return secondId;
|
|
256
|
-
if (secondCollection === source.collection && secondId === source.id && firstCollection === targetName) return firstId;
|
|
257
|
-
}
|
|
258
|
-
function resolveRecordReference(first, second) {
|
|
259
|
-
return {
|
|
260
|
-
collection: first,
|
|
261
|
-
id: second
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
async function linkedIds(source, targetName) {
|
|
265
|
-
const rows = await (await initializedDatabase()).select("SELECT collections, ids FROM links");
|
|
266
|
-
const ids = [];
|
|
267
|
-
for (const row of rows) {
|
|
268
|
-
const id = linkedIdFromRow(row, source, targetName);
|
|
269
|
-
if (id) ids.push(id);
|
|
270
|
-
}
|
|
271
|
-
return ids;
|
|
272
|
-
}
|
|
273
|
-
function toView(collection$1, raw) {
|
|
274
|
-
return {
|
|
275
|
-
id: raw.id,
|
|
276
|
-
collection: raw.collection,
|
|
277
|
-
createdAt: raw.createdAt,
|
|
278
|
-
updatedAt: raw.updatedAt,
|
|
279
|
-
data: collection$1.validator.parse(raw.data),
|
|
280
|
-
links: raw.links
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
function isRecordLoaded(entry) {
|
|
284
|
-
return entry.value !== void 0;
|
|
285
|
-
}
|
|
286
|
-
function isRelatedPath(left, right) {
|
|
287
|
-
return left === right || left.startsWith(`${right}.`) || right.startsWith(`${left}.`);
|
|
288
|
-
}
|
|
289
|
-
var Collection = class {
|
|
290
|
-
validator;
|
|
291
|
-
records = /* @__PURE__ */ new Map();
|
|
292
|
-
lists = /* @__PURE__ */ new Map();
|
|
293
|
-
fields = /* @__PURE__ */ new Map();
|
|
294
|
-
constructor(name, shape) {
|
|
295
|
-
this.name = name;
|
|
296
|
-
this.shape = shape;
|
|
297
|
-
if (collectionsByName.has(name)) throw new Error(`Collection "${name}" is already registered`);
|
|
298
|
-
collectionsByName.set(name, this);
|
|
299
|
-
this.validator = schema(shape);
|
|
300
|
-
}
|
|
301
|
-
validateListOptions(options) {
|
|
302
|
-
if (!options) return;
|
|
303
|
-
if (options.sortBy && options.sortBy !== "createdAt" && options.sortBy !== "updatedAt" && !(options.sortBy in this.shape)) throw new Error(`Invalid sort field "${String(options.sortBy)}" for collection "${this.name}"`);
|
|
304
|
-
for (const key of Object.keys(options.where ?? {})) if (!(key in this.shape)) throw new Error(`Invalid where field "${key}" for collection "${this.name}"`);
|
|
305
|
-
}
|
|
306
|
-
getRecordEntry(id) {
|
|
307
|
-
const key = recordKey(this.name, id);
|
|
308
|
-
const existing = this.records.get(key);
|
|
309
|
-
if (existing) return existing;
|
|
310
|
-
const entry = createEntry();
|
|
311
|
-
this.records.set(key, entry);
|
|
312
|
-
return entry;
|
|
313
|
-
}
|
|
314
|
-
getListEntry(options) {
|
|
315
|
-
const normalized = normalizeListOptions(options);
|
|
316
|
-
const key = listKey(this, options);
|
|
317
|
-
const existing = this.lists.get(key);
|
|
318
|
-
if (existing) return existing;
|
|
319
|
-
const entry = {
|
|
320
|
-
...createEntry(),
|
|
321
|
-
dependencies: compileListDependencies(this.name, normalized)
|
|
322
|
-
};
|
|
323
|
-
this.lists.set(key, entry);
|
|
324
|
-
return entry;
|
|
325
|
-
}
|
|
326
|
-
getFieldEntry(id, path) {
|
|
327
|
-
const key = recordKey(this.name, id);
|
|
328
|
-
let entries = this.fields.get(key);
|
|
329
|
-
if (!entries) {
|
|
330
|
-
entries = /* @__PURE__ */ new Map();
|
|
331
|
-
this.fields.set(key, entries);
|
|
332
|
-
}
|
|
333
|
-
const existing = entries.get(path);
|
|
334
|
-
if (existing) return existing;
|
|
335
|
-
const entry = {
|
|
336
|
-
version: 0,
|
|
337
|
-
listeners: /* @__PURE__ */ new Set()
|
|
338
|
-
};
|
|
339
|
-
entries.set(path, entry);
|
|
340
|
-
return entry;
|
|
341
|
-
}
|
|
342
|
-
notifyFields(id, changedPaths) {
|
|
343
|
-
const entries = this.fields.get(recordKey(this.name, id));
|
|
344
|
-
if (!entries) return;
|
|
345
|
-
for (const [path, entry] of entries) {
|
|
346
|
-
if (changedPaths !== null && ![...changedPaths].some((changedPath) => isRelatedPath(path, changedPath))) continue;
|
|
347
|
-
entry.version += 1;
|
|
348
|
-
notify(entry);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
setRecordValue(id, value, changedPaths = null) {
|
|
352
|
-
const entry = this.getRecordEntry(id);
|
|
353
|
-
const changed = entryChanged(entry, value);
|
|
354
|
-
entry.value = value;
|
|
355
|
-
entry.error = void 0;
|
|
356
|
-
entry.promise = null;
|
|
357
|
-
if (changed) {
|
|
358
|
-
entry.version += 1;
|
|
359
|
-
notify(entry);
|
|
360
|
-
this.notifyFields(id, changedPaths);
|
|
361
|
-
}
|
|
362
|
-
return changed;
|
|
363
|
-
}
|
|
364
|
-
async rawRecord(id, links) {
|
|
365
|
-
const row = await (await initializedDatabase()).get("SELECT id, data, createdAt, updatedAt FROM collections WHERE collection = ? AND id = ?", [this.name, id]);
|
|
366
|
-
if (!row) return null;
|
|
367
|
-
const persisted = parsePersistedRow(row);
|
|
368
|
-
const nextLinks = { ...this.peekRecord(id)?.links ?? {} };
|
|
369
|
-
for (const target of links ?? []) nextLinks[target.name] = await linkedIds({
|
|
370
|
-
collection: this.name,
|
|
371
|
-
id
|
|
372
|
-
}, target.collection.name);
|
|
373
|
-
return {
|
|
374
|
-
id: persisted.id,
|
|
375
|
-
collection: this.name,
|
|
376
|
-
createdAt: persisted.createdAt,
|
|
377
|
-
updatedAt: persisted.updatedAt,
|
|
378
|
-
data: decodeRecordData(this, persisted.data),
|
|
379
|
-
links: nextLinks
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
async loadRecord(id, options) {
|
|
383
|
-
const links = (options?.links ?? []).map((collection$1) => ({
|
|
384
|
-
name: collection$1.name,
|
|
385
|
-
collection: collection$1
|
|
386
|
-
}));
|
|
387
|
-
const entry = this.getRecordEntry(id);
|
|
388
|
-
if (isRecordLoaded(entry) && links.every((link$1) => entry.value?.links[link$1.name] !== void 0)) return entry.value ?? null;
|
|
389
|
-
if (entry.promise) return entry.promise;
|
|
390
|
-
const promise = this.rawRecord(id, links).then((value) => {
|
|
391
|
-
this.setRecordValue(id, value);
|
|
392
|
-
return value;
|
|
393
|
-
}).catch((error) => {
|
|
394
|
-
entry.error = error;
|
|
395
|
-
entry.promise = null;
|
|
396
|
-
entry.version += 1;
|
|
397
|
-
notify(entry);
|
|
398
|
-
throw error;
|
|
399
|
-
});
|
|
400
|
-
entry.promise = promise;
|
|
401
|
-
return promise;
|
|
402
|
-
}
|
|
403
|
-
async loadListRows(options) {
|
|
404
|
-
this.validateListOptions(options);
|
|
405
|
-
const entry = this.getListEntry(options);
|
|
406
|
-
if (entry.value !== void 0) return entry.value;
|
|
407
|
-
if (entry.promise) return entry.promise;
|
|
408
|
-
const promise = this.selectListRows(options).then((rows) => {
|
|
409
|
-
for (const row of rows) this.setRecordValue(row.id, row);
|
|
410
|
-
const ids = rows.map((row) => row.id);
|
|
411
|
-
const changed = entryChanged(entry, ids);
|
|
412
|
-
entry.value = ids;
|
|
413
|
-
entry.error = void 0;
|
|
414
|
-
entry.promise = null;
|
|
415
|
-
if (changed) {
|
|
416
|
-
entry.version += 1;
|
|
417
|
-
notify(entry);
|
|
418
|
-
}
|
|
419
|
-
return ids;
|
|
420
|
-
}).catch((error) => {
|
|
421
|
-
entry.error = error;
|
|
422
|
-
entry.promise = null;
|
|
423
|
-
entry.version += 1;
|
|
424
|
-
notify(entry);
|
|
425
|
-
throw error;
|
|
426
|
-
});
|
|
427
|
-
entry.promise = promise;
|
|
428
|
-
return promise;
|
|
429
|
-
}
|
|
430
|
-
async selectListRows(options) {
|
|
431
|
-
const rows = await (await initializedDatabase()).select("SELECT id, data, createdAt, updatedAt FROM collections WHERE collection = ? ORDER BY createdAt ASC", [this.name]);
|
|
432
|
-
const normalized = normalizeListOptions(options);
|
|
433
|
-
const views = rows.map((row) => {
|
|
434
|
-
const persisted = parsePersistedRow(row);
|
|
435
|
-
return {
|
|
436
|
-
id: persisted.id,
|
|
437
|
-
collection: this.name,
|
|
438
|
-
createdAt: persisted.createdAt,
|
|
439
|
-
updatedAt: persisted.updatedAt,
|
|
440
|
-
data: decodeRecordData(this, persisted.data),
|
|
441
|
-
links: this.peekRecord(persisted.id)?.links ?? {}
|
|
442
|
-
};
|
|
443
|
-
});
|
|
444
|
-
return this.applyListOptions(views, normalized);
|
|
445
|
-
}
|
|
446
|
-
applyListOptions(records, options) {
|
|
447
|
-
let next = records;
|
|
448
|
-
if (options.where) next = next.filter((record) => {
|
|
449
|
-
for (const [key, clauses] of Object.entries(options.where ?? {})) {
|
|
450
|
-
const value = getPathValue(record, key);
|
|
451
|
-
if (!clauses.every(([operator, operand]) => matchesWhere(value, operator, operand))) return false;
|
|
452
|
-
}
|
|
453
|
-
return true;
|
|
454
|
-
});
|
|
455
|
-
if (options.sortBy) {
|
|
456
|
-
const direction = options.order === "desc" ? -1 : 1;
|
|
457
|
-
next = [...next].sort((left, right) => {
|
|
458
|
-
const leftValue = getPathValue(left, options.sortBy);
|
|
459
|
-
const rightValue = getPathValue(right, options.sortBy);
|
|
460
|
-
if (leftValue === rightValue) return 0;
|
|
461
|
-
return stableStringify(leftValue) < stableStringify(rightValue) ? -1 * direction : direction;
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
return typeof options.take === "number" ? next.slice(0, options.take) : next;
|
|
465
|
-
}
|
|
466
|
-
async refreshListEntry(key, entry) {
|
|
467
|
-
const rows = await this.selectListRows(entry.dependencies.options);
|
|
468
|
-
for (const row of rows) this.setRecordValue(row.id, row);
|
|
469
|
-
const ids = rows.map((row) => row.id);
|
|
470
|
-
const changed = entryChanged(entry, ids);
|
|
471
|
-
entry.value = ids;
|
|
472
|
-
entry.error = void 0;
|
|
473
|
-
entry.promise = null;
|
|
474
|
-
if (changed) {
|
|
475
|
-
entry.version += 1;
|
|
476
|
-
notify(entry);
|
|
477
|
-
}
|
|
478
|
-
this.lists.set(key, entry);
|
|
479
|
-
}
|
|
480
|
-
async invalidateChanges(recordChanges, edgeChanges = []) {
|
|
481
|
-
for (const change of recordChanges) this.setRecordValue(change.id, change.next, change.changedPaths);
|
|
482
|
-
for (const change of edgeChanges) await this.refreshLoadedLinks(change);
|
|
483
|
-
const candidateKeys = /* @__PURE__ */ new Set();
|
|
484
|
-
for (const [key, entry] of this.lists.entries()) if (recordChanges.some((change) => listMayBeAffectedByRecordChange(entry.dependencies, change, entry.value))) candidateKeys.add(key);
|
|
485
|
-
for (const key of candidateKeys) {
|
|
486
|
-
const entry = this.lists.get(key);
|
|
487
|
-
if (entry) await this.refreshListEntry(key, entry);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
async refreshLoadedLinks(change) {
|
|
491
|
-
const affected = [change.left, change.right].filter((record) => record.collection === this.name);
|
|
492
|
-
for (const record of affected) {
|
|
493
|
-
const current = this.getRecordEntry(record.id).value;
|
|
494
|
-
if (!current) continue;
|
|
495
|
-
const linkNames = Object.keys(current.links);
|
|
496
|
-
if (linkNames.length === 0) continue;
|
|
497
|
-
const nextLinks = { ...current.links };
|
|
498
|
-
for (const name of linkNames) nextLinks[name] = await linkedIds({
|
|
499
|
-
collection: this.name,
|
|
500
|
-
id: record.id
|
|
501
|
-
}, name);
|
|
502
|
-
this.setRecordValue(record.id, {
|
|
503
|
-
...current,
|
|
504
|
-
links: nextLinks
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
peekRecord(id) {
|
|
509
|
-
return this.getRecordEntry(id).value;
|
|
510
|
-
}
|
|
511
|
-
async insert(value) {
|
|
512
|
-
const database$1 = await initializedDatabase();
|
|
513
|
-
const parsed = this.validator.parse(value);
|
|
514
|
-
const id = crypto.randomUUID();
|
|
515
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
516
|
-
const parameters = [
|
|
517
|
-
this.name,
|
|
518
|
-
id,
|
|
519
|
-
encodeRecordData(this, parsed),
|
|
520
|
-
timestamp,
|
|
521
|
-
timestamp
|
|
522
|
-
];
|
|
523
|
-
await database$1.execute(`INSERT INTO collections (collection, id, data, createdAt, updatedAt, version)
|
|
524
|
-
VALUES (?, ?, ?, ?, ?, 1)`, parameters);
|
|
525
|
-
const next = {
|
|
526
|
-
id,
|
|
527
|
-
collection: this.name,
|
|
528
|
-
createdAt: timestamp,
|
|
529
|
-
updatedAt: timestamp,
|
|
530
|
-
data: parsed,
|
|
531
|
-
links: {}
|
|
532
|
-
};
|
|
533
|
-
await this.invalidateChanges([createRecordChange(this.name, id, null, next)]);
|
|
534
|
-
return id;
|
|
535
|
-
}
|
|
536
|
-
async update(id, value) {
|
|
537
|
-
const database$1 = await initializedDatabase();
|
|
538
|
-
if (Object.keys(value).filter((key) => key in this.shape).length === 0) return;
|
|
539
|
-
const previous = await this.rawRecord(id);
|
|
540
|
-
if (!previous) throw new Error(`Cannot update missing collection record "${this.name}:${id}"`);
|
|
541
|
-
const nextData = this.validator.parse({
|
|
542
|
-
...previous.data,
|
|
543
|
-
...value
|
|
544
|
-
});
|
|
545
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
546
|
-
await database$1.execute(`UPDATE collections
|
|
547
|
-
SET data = ?, updatedAt = ?, version = version + 1
|
|
548
|
-
WHERE collection = ? AND id = ?`, [
|
|
549
|
-
encodeRecordData(this, nextData),
|
|
550
|
-
timestamp,
|
|
551
|
-
this.name,
|
|
552
|
-
id
|
|
553
|
-
]);
|
|
554
|
-
const next = {
|
|
555
|
-
...previous,
|
|
556
|
-
updatedAt: timestamp,
|
|
557
|
-
data: nextData
|
|
558
|
-
};
|
|
559
|
-
await this.invalidateChanges([createRecordChange(this.name, id, previous, next)]);
|
|
560
|
-
}
|
|
561
|
-
async delete(id) {
|
|
562
|
-
const database$1 = await initializedDatabase();
|
|
563
|
-
const previous = await this.rawRecord(id);
|
|
564
|
-
if (!previous) throw new Error(`Cannot delete missing collection record "${this.name}:${id}"`);
|
|
565
|
-
await database$1.transaction(async () => {
|
|
566
|
-
await database$1.execute("DELETE FROM collections WHERE collection = ? AND id = ?", [this.name, id]);
|
|
567
|
-
await this.deleteLinksForRecord(id);
|
|
568
|
-
});
|
|
569
|
-
await this.invalidateChanges([createRecordChange(this.name, id, previous, null)]);
|
|
570
|
-
}
|
|
571
|
-
async all() {
|
|
572
|
-
return (await this.selectListRows()).map((row) => toView(this, row));
|
|
573
|
-
}
|
|
574
|
-
async get(id) {
|
|
575
|
-
const row = await this.loadRecord(id);
|
|
576
|
-
return row ? toView(this, row) : null;
|
|
577
|
-
}
|
|
578
|
-
subscribeRecord(id, listener) {
|
|
579
|
-
const entry = this.getRecordEntry(id);
|
|
580
|
-
entry.listeners.add(listener);
|
|
581
|
-
return () => {
|
|
582
|
-
entry.listeners.delete(listener);
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
subscribeList(options, listener) {
|
|
586
|
-
const entry = this.getListEntry(options);
|
|
587
|
-
entry.listeners.add(listener);
|
|
588
|
-
return () => {
|
|
589
|
-
entry.listeners.delete(listener);
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
subscribeField(id, path, listener) {
|
|
593
|
-
const entry = this.getFieldEntry(id, path);
|
|
594
|
-
entry.listeners.add(listener);
|
|
595
|
-
return () => {
|
|
596
|
-
entry.listeners.delete(listener);
|
|
597
|
-
if (entry.listeners.size > 0) return;
|
|
598
|
-
const key = recordKey(this.name, id);
|
|
599
|
-
const entries = this.fields.get(key);
|
|
600
|
-
if (!entries) return;
|
|
601
|
-
entries.delete(path);
|
|
602
|
-
if (entries.size === 0) this.fields.delete(key);
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
getRecordVersion(id) {
|
|
606
|
-
return this.getRecordEntry(id).version;
|
|
607
|
-
}
|
|
608
|
-
getListVersion(options) {
|
|
609
|
-
return this.getListEntry(options).version;
|
|
610
|
-
}
|
|
611
|
-
getFieldVersion(id, path) {
|
|
612
|
-
return this.getFieldEntry(id, path).version;
|
|
613
|
-
}
|
|
614
|
-
getRecordSnapshot(id, options) {
|
|
615
|
-
const row = this.peekRecord(id);
|
|
616
|
-
if (!row) return row ?? null;
|
|
617
|
-
if ((options?.links ?? []).some((link$1) => row.links[link$1.name] === void 0)) return;
|
|
618
|
-
return toView(this, row);
|
|
619
|
-
}
|
|
620
|
-
getListSnapshot(options) {
|
|
621
|
-
return this.getListEntry(options).value;
|
|
622
|
-
}
|
|
623
|
-
async ensureRecord(id, options) {
|
|
624
|
-
return this.loadRecord(id, options);
|
|
625
|
-
}
|
|
626
|
-
async ensureList(options) {
|
|
627
|
-
return this.loadListRows(options);
|
|
628
|
-
}
|
|
629
|
-
async deleteLinksForRecord(id) {
|
|
630
|
-
const database$1 = await initializedDatabase();
|
|
631
|
-
const rows = await database$1.select("SELECT id, collections, ids FROM links");
|
|
632
|
-
const deleted = [];
|
|
633
|
-
for (const row of rows) {
|
|
634
|
-
if (!linkIncludesRecord(row, this.name, id)) continue;
|
|
635
|
-
const [firstCollection, secondCollection] = decodePair(rowValue(row, "collections"));
|
|
636
|
-
const [firstId, secondId] = decodePair(rowValue(row, "ids"));
|
|
637
|
-
deleted.push({
|
|
638
|
-
type: "unlink",
|
|
639
|
-
left: {
|
|
640
|
-
collection: firstCollection,
|
|
641
|
-
id: firstId
|
|
642
|
-
},
|
|
643
|
-
right: {
|
|
644
|
-
collection: secondCollection,
|
|
645
|
-
id: secondId
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
await database$1.execute("DELETE FROM links WHERE id = ?", [rowValue(row, "id")]);
|
|
649
|
-
}
|
|
650
|
-
for (const change of deleted) await notifyEdgeChange(change);
|
|
651
|
-
}
|
|
652
|
-
};
|
|
653
|
-
function matchesWhere(value, operator, operand) {
|
|
654
|
-
switch (operator) {
|
|
655
|
-
case "equals": return value === operand;
|
|
656
|
-
case "greater-than": return typeof value === "number" && typeof operand === "number" && value > operand;
|
|
657
|
-
case "less-than": return typeof value === "number" && typeof operand === "number" && value < operand;
|
|
658
|
-
case "contains": return typeof value === "string" && typeof operand === "string" && value.includes(operand);
|
|
659
|
-
default: throw new Error(`Invalid where operator "${operator}"`);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
function collection(name, shape) {
|
|
663
|
-
return new Collection(name, shape);
|
|
664
|
-
}
|
|
665
|
-
async function notifyEdgeChange(change) {
|
|
666
|
-
const names = new Set([change.left.collection, change.right.collection]);
|
|
667
|
-
for (const name of names) await collectionsByName.get(name)?.invalidateChanges([], [change]);
|
|
668
|
-
}
|
|
669
|
-
async function link(firstCollection, firstId, secondCollection, secondId) {
|
|
670
|
-
const row = storedLinkFor(resolveRecordReference(firstCollection, firstId), resolveRecordReference(secondCollection, secondId));
|
|
671
|
-
await (await initializedDatabase()).execute("INSERT OR IGNORE INTO links (id, collections, ids, createdAt) VALUES (?, ?, ?, ?)", [
|
|
672
|
-
row.id,
|
|
673
|
-
row.collections,
|
|
674
|
-
row.ids,
|
|
675
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
676
|
-
]);
|
|
677
|
-
await notifyEdgeChange({
|
|
678
|
-
type: "link",
|
|
679
|
-
left: {
|
|
680
|
-
collection: firstCollection.name,
|
|
681
|
-
id: firstId
|
|
682
|
-
},
|
|
683
|
-
right: {
|
|
684
|
-
collection: secondCollection.name,
|
|
685
|
-
id: secondId
|
|
686
|
-
}
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
async function unlink(firstCollection, firstId, secondCollection, secondId) {
|
|
690
|
-
const row = storedLinkFor(resolveRecordReference(firstCollection, firstId), resolveRecordReference(secondCollection, secondId));
|
|
691
|
-
await (await initializedDatabase()).execute("DELETE FROM links WHERE id = ?", [row.id]);
|
|
692
|
-
await notifyEdgeChange({
|
|
693
|
-
type: "unlink",
|
|
694
|
-
left: {
|
|
695
|
-
collection: firstCollection.name,
|
|
696
|
-
id: firstId
|
|
697
|
-
},
|
|
698
|
-
right: {
|
|
699
|
-
collection: secondCollection.name,
|
|
700
|
-
id: secondId
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
async function isLinked(firstCollection, firstId, secondCollection, secondId) {
|
|
705
|
-
const row = storedLinkFor(resolveRecordReference(firstCollection, firstId), resolveRecordReference(secondCollection, secondId));
|
|
706
|
-
return await (await initializedDatabase()).get("SELECT id FROM links WHERE id = ?", [row.id]) !== void 0;
|
|
707
|
-
}
|
|
708
|
-
function useList(collection$1, options) {
|
|
709
|
-
collection$1.validateListOptions(options);
|
|
710
|
-
useSyncExternalStore((listener) => collection$1.subscribeList(options, listener), () => collection$1.getListVersion(options), () => collection$1.getListVersion(options));
|
|
711
|
-
const entry = collection$1.getListEntry(options);
|
|
712
|
-
if (entry.error) throw entry.error;
|
|
713
|
-
if (entry.value === void 0) use(collection$1.ensureList(options));
|
|
714
|
-
return collection$1.getListSnapshot(options) ?? [];
|
|
715
|
-
}
|
|
716
|
-
function useView(collection$1, id, options) {
|
|
717
|
-
useSyncExternalStore((listener) => collection$1.subscribeRecord(id, listener), () => collection$1.getRecordVersion(id), () => collection$1.getRecordVersion(id));
|
|
718
|
-
const entry = collection$1.getRecordEntry(id);
|
|
719
|
-
if (entry.error) throw entry.error;
|
|
720
|
-
if (collection$1.getRecordSnapshot(id, options) === void 0) use(collection$1.ensureRecord(id, options));
|
|
721
|
-
return collection$1.getRecordSnapshot(id, options);
|
|
722
|
-
}
|
|
723
|
-
function useField(collection$1, id, key) {
|
|
724
|
-
useSyncExternalStore((listener) => collection$1.subscribeField(id, key, listener), () => collection$1.getFieldVersion(id, key), () => collection$1.getFieldVersion(id, key));
|
|
725
|
-
const entry = collection$1.getRecordEntry(id);
|
|
726
|
-
if (entry.error) throw entry.error;
|
|
727
|
-
if (entry.value === void 0) use(collection$1.ensureRecord(id));
|
|
728
|
-
return (collection$1.getRecordSnapshot(id)?.data)?.[key];
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
//#endregion
|
|
732
|
-
//#region src/state/setting.ts
|
|
733
|
-
function settingKey(key) {
|
|
734
|
-
return `bleam.apps.${currentAppStorageKey()}.${key}`;
|
|
735
|
-
}
|
|
736
|
-
function readSettingValue(key, fallback) {
|
|
737
|
-
const value = NSUserDefaults.standardUserDefaults.objectForKey(settingKey(key));
|
|
738
|
-
return value === null || value === void 0 ? fallback : value;
|
|
739
|
-
}
|
|
740
|
-
function writeSettingValue(key, value) {
|
|
741
|
-
const userDefaults = NSUserDefaults.standardUserDefaults;
|
|
742
|
-
if (value === void 0) {
|
|
743
|
-
userDefaults.removeObjectForKey(settingKey(key));
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
userDefaults.setObjectForKey(value, settingKey(key));
|
|
747
|
-
}
|
|
748
|
-
function setting(key, initialValue) {
|
|
749
|
-
const store = new LiveStore(readSettingValue(key, initialValue));
|
|
750
|
-
return {
|
|
751
|
-
get: store.get.bind(store),
|
|
752
|
-
subscribe: store.subscribe.bind(store),
|
|
753
|
-
set(value) {
|
|
754
|
-
writeSettingValue(key, value);
|
|
755
|
-
store.set(value);
|
|
756
|
-
},
|
|
757
|
-
update(updater) {
|
|
758
|
-
this.set(updater(store.get()));
|
|
759
|
-
}
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
function useSetting(value) {
|
|
763
|
-
return useAtom(value);
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
//#endregion
|
|
767
|
-
export { isLinked as a, useField as c, LiveStore as d, atom as f, collection as i, useList as l, useStore as m, useSetting as n, link as o, useAtom as p, Collection as r, unlink as s, setting as t, useView as u };
|