cogsbox-shape 0.5.197 → 0.5.199
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/cogsbox-shape-db/dist/connect.js +32 -19
- package/cogsbox-shape-db/dist/table-db.d.ts +11 -3
- package/cogsbox-shape-db/dist/table-db.js +41 -14
- package/cogsbox-shape-state/dist/index.d.ts +1 -0
- package/cogsbox-shape-state/dist/index.js +1 -0
- package/cogsbox-shape-state/dist/plugin.d.ts +34037 -0
- package/cogsbox-shape-state/dist/plugin.js +28 -0
- package/dist/schema.d.ts +7 -7
- package/package.json +11 -3
|
@@ -129,14 +129,13 @@ function createViewHydrator(db, registry, baseRegistryKey, selection) {
|
|
|
129
129
|
}
|
|
130
130
|
return meta;
|
|
131
131
|
};
|
|
132
|
-
const hydrate = async (
|
|
133
|
-
if (!
|
|
134
|
-
return
|
|
132
|
+
const hydrate = async (rows, currentRegistryKey, currentSelection) => {
|
|
133
|
+
if (!rows.length || typeof currentSelection !== "object")
|
|
134
|
+
return rows;
|
|
135
135
|
const currentEntry = registry[currentRegistryKey];
|
|
136
136
|
if (!currentEntry)
|
|
137
|
-
return
|
|
137
|
+
return rows;
|
|
138
138
|
const currentMeta = getMeta(currentRegistryKey);
|
|
139
|
-
const hydrated = { ...row };
|
|
140
139
|
for (const [relationKey, relationSelection] of Object.entries(currentSelection)) {
|
|
141
140
|
if (!relationSelection)
|
|
142
141
|
continue;
|
|
@@ -155,27 +154,41 @@ function createViewHydrator(db, registry, baseRegistryKey, selection) {
|
|
|
155
154
|
if (!targetClientKey)
|
|
156
155
|
continue;
|
|
157
156
|
const targetDbName = dbNameForClientKey(targetMeta, targetClientKey);
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
const isCollection = ["hasMany", "manyToMany"].includes(relationConfig.type);
|
|
158
|
+
const fromValues = rows
|
|
159
|
+
.map((row) => row[fromDbName])
|
|
160
|
+
.filter((v) => v !== undefined && v !== null);
|
|
161
|
+
if (fromValues.length === 0) {
|
|
162
|
+
for (const row of rows) {
|
|
163
|
+
row[relationKey] = isCollection ? [] : null;
|
|
164
|
+
}
|
|
163
165
|
continue;
|
|
164
166
|
}
|
|
165
167
|
const qb = db;
|
|
166
168
|
const relatedRows = (await qb
|
|
167
169
|
.selectFrom(targetMeta.tableName)
|
|
168
170
|
.selectAll()
|
|
169
|
-
.where(targetDbName, "
|
|
171
|
+
.where(targetDbName, "in", fromValues)
|
|
170
172
|
.execute());
|
|
171
|
-
const hydratedRelated = await
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
const hydratedRelated = await hydrate(relatedRows, targetRegistryKey, relationSelection);
|
|
174
|
+
const grouped = new Map();
|
|
175
|
+
for (const related of hydratedRelated) {
|
|
176
|
+
const key = related[targetDbName];
|
|
177
|
+
if (!grouped.has(key))
|
|
178
|
+
grouped.set(key, []);
|
|
179
|
+
grouped.get(key).push(related);
|
|
180
|
+
}
|
|
181
|
+
for (const row of rows) {
|
|
182
|
+
const fk = row[fromDbName];
|
|
183
|
+
const matches = fk !== undefined && fk !== null ? grouped.get(fk) : undefined;
|
|
184
|
+
row[relationKey] = isCollection
|
|
185
|
+
? (matches ?? [])
|
|
186
|
+
: (matches?.[0] ?? null);
|
|
187
|
+
}
|
|
175
188
|
}
|
|
176
|
-
return
|
|
189
|
+
return rows;
|
|
177
190
|
};
|
|
178
|
-
return (
|
|
191
|
+
return (rows) => hydrate(rows, baseRegistryKey, selection);
|
|
179
192
|
}
|
|
180
193
|
export function connect(box, db) {
|
|
181
194
|
const result = {};
|
|
@@ -198,7 +211,7 @@ export function connect(box, db) {
|
|
|
198
211
|
const registry = view.__registry;
|
|
199
212
|
const baseTable = view.baseTable;
|
|
200
213
|
const viewSelection = view.viewSelection;
|
|
201
|
-
const
|
|
214
|
+
const hydrateRows = registry && baseTable && viewSelection
|
|
202
215
|
? createViewHydrator(db, registry, baseTable, viewSelection)
|
|
203
216
|
: undefined;
|
|
204
217
|
const viewDb = new TableDB(db, viewMeta, {
|
|
@@ -207,7 +220,7 @@ export function connect(box, db) {
|
|
|
207
220
|
parseForDb: viewTransforms.parseForDb ?? ((r) => r),
|
|
208
221
|
parsePatchForDb: viewTransforms.parsePatchForDb ?? viewTransforms.toDb ?? ((r) => r),
|
|
209
222
|
parseFromDb: viewTransforms.parseFromDb ?? ((r) => r),
|
|
210
|
-
}, reconcile,
|
|
223
|
+
}, reconcile, hydrateRows);
|
|
211
224
|
return new Proxy(view, {
|
|
212
225
|
get(target, prop, receiver) {
|
|
213
226
|
if (prop in viewDb) {
|
|
@@ -5,13 +5,13 @@ type RequiredKeys<T> = {
|
|
|
5
5
|
[K in keyof T]-?: Record<string, never> extends Pick<T, K> ? never : K;
|
|
6
6
|
}[keyof T];
|
|
7
7
|
type InsertDbOnlyArgs<T extends Record<string, unknown>> = keyof T extends never ? [] : RequiredKeys<T> extends never ? [dbOnlyData?: Partial<T>] : [dbOnlyData: T];
|
|
8
|
-
export type TableDBApi<TClient extends Record<string, unknown>, TCreate, TDbOnly extends Record<string, unknown> = Record<string, never>> = Pick<TableDB<TClient, TCreate, TDbOnly>, "findMany" | "findById" | "byId" | "insert" | "create" | "update" | "delete" | "count" | "reconcileIds">;
|
|
8
|
+
export type TableDBApi<TClient extends Record<string, unknown>, TCreate, TDbOnly extends Record<string, unknown> = Record<string, never>> = Pick<TableDB<TClient, TCreate, TDbOnly>, "findMany" | "findById" | "byId" | "insert" | "insertOrIgnore" | "create" | "update" | "delete" | "deleteMany" | "count" | "reconcileIds" | "kysely">;
|
|
9
9
|
export declare class TableDB<TClient extends Record<string, unknown>, TCreate, TDbOnly extends Record<string, unknown> = Record<string, never>> {
|
|
10
10
|
private db;
|
|
11
11
|
private meta;
|
|
12
12
|
private transforms;
|
|
13
13
|
private reconcile?;
|
|
14
|
-
private
|
|
14
|
+
private hydrateRows?;
|
|
15
15
|
constructor(db: Kysely<any>, meta: TableMeta, transforms: {
|
|
16
16
|
toClient: (row: Record<string, unknown>) => TClient;
|
|
17
17
|
toDb: (row: Record<string, unknown>) => Record<string, unknown>;
|
|
@@ -20,7 +20,8 @@ export declare class TableDB<TClient extends Record<string, unknown>, TCreate, T
|
|
|
20
20
|
parseFromDb: (data: Record<string, unknown>) => TClient;
|
|
21
21
|
}, reconcile?: ((clientData: unknown) => {
|
|
22
22
|
withServer: (serverData: unknown) => unknown;
|
|
23
|
-
}) | undefined,
|
|
23
|
+
}) | undefined, hydrateRows?: ((rows: Record<string, unknown>[]) => Promise<Record<string, unknown>[]>) | undefined);
|
|
24
|
+
get kysely(): Kysely<any>;
|
|
24
25
|
findMany(opts?: FindManyOpts<TClient>): Promise<TClient[]>;
|
|
25
26
|
findById(id: unknown): Promise<TClient | null>;
|
|
26
27
|
byId(id: unknown): {
|
|
@@ -55,6 +56,13 @@ export declare class TableDB<TClient extends Record<string, unknown>, TCreate, T
|
|
|
55
56
|
delete(id: unknown): Promise<{
|
|
56
57
|
deleted: boolean;
|
|
57
58
|
}>;
|
|
59
|
+
deleteMany(where: WhereInput<Partial<TClient>>): Promise<{
|
|
60
|
+
deleted: number;
|
|
61
|
+
}>;
|
|
62
|
+
insertOrIgnore(data: TCreate, ...args: InsertDbOnlyArgs<TDbOnly>): Promise<{
|
|
63
|
+
ids: () => Promise<Record<string, unknown>>;
|
|
64
|
+
full: () => Promise<TClient>;
|
|
65
|
+
}>;
|
|
58
66
|
count(where?: WhereInput<Partial<TClient>>): Promise<number>;
|
|
59
67
|
}
|
|
60
68
|
export {};
|
|
@@ -6,13 +6,16 @@ export class TableDB {
|
|
|
6
6
|
meta;
|
|
7
7
|
transforms;
|
|
8
8
|
reconcile;
|
|
9
|
-
|
|
10
|
-
constructor(db, meta, transforms, reconcile,
|
|
9
|
+
hydrateRows;
|
|
10
|
+
constructor(db, meta, transforms, reconcile, hydrateRows) {
|
|
11
11
|
this.db = db;
|
|
12
12
|
this.meta = meta;
|
|
13
13
|
this.transforms = transforms;
|
|
14
14
|
this.reconcile = reconcile;
|
|
15
|
-
this.
|
|
15
|
+
this.hydrateRows = hydrateRows;
|
|
16
|
+
}
|
|
17
|
+
get kysely() {
|
|
18
|
+
return this.db;
|
|
16
19
|
}
|
|
17
20
|
async findMany(opts) {
|
|
18
21
|
const qb = this.db;
|
|
@@ -38,8 +41,8 @@ export class TableDB {
|
|
|
38
41
|
query = query.offset(opts.offset);
|
|
39
42
|
}
|
|
40
43
|
const rows = (await query.execute());
|
|
41
|
-
const hydratedRows = this.
|
|
42
|
-
? await
|
|
44
|
+
const hydratedRows = this.hydrateRows
|
|
45
|
+
? await this.hydrateRows(rows)
|
|
43
46
|
: rows;
|
|
44
47
|
return hydratedRows.map((r) => this.transforms.parseFromDb(r));
|
|
45
48
|
}
|
|
@@ -59,8 +62,10 @@ export class TableDB {
|
|
|
59
62
|
const row = rows[0] ?? null;
|
|
60
63
|
if (!row)
|
|
61
64
|
return null;
|
|
62
|
-
const
|
|
63
|
-
|
|
65
|
+
const hydratedRows = this.hydrateRows
|
|
66
|
+
? await this.hydrateRows([row])
|
|
67
|
+
: [row];
|
|
68
|
+
return this.transforms.parseFromDb(hydratedRows[0]);
|
|
64
69
|
}
|
|
65
70
|
byId(id) {
|
|
66
71
|
return {
|
|
@@ -83,7 +88,7 @@ export class TableDB {
|
|
|
83
88
|
const dbOnlyData = args[0];
|
|
84
89
|
return this.insertIds(data, dbOnlyData);
|
|
85
90
|
}
|
|
86
|
-
async insertIds(data, dbOnlyData) {
|
|
91
|
+
async insertIds(data, dbOnlyData, opts) {
|
|
87
92
|
const dbData = this.transforms.parseForDb(data);
|
|
88
93
|
const parsedDbOnlyData = this.parseDbOnlyData(dbOnlyData, {
|
|
89
94
|
requireRequired: true,
|
|
@@ -102,10 +107,11 @@ export class TableDB {
|
|
|
102
107
|
}
|
|
103
108
|
Object.assign(insertData, parsedDbOnlyData);
|
|
104
109
|
const qb = this.db;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
.
|
|
108
|
-
|
|
110
|
+
let query = qb.insertInto(this.meta.tableName).values(insertData);
|
|
111
|
+
if (opts?.onConflict === "ignore") {
|
|
112
|
+
query = query.onConflict((oc) => oc.doNothing());
|
|
113
|
+
}
|
|
114
|
+
const result = await query.execute();
|
|
109
115
|
const insertId = result[0]?.insertId;
|
|
110
116
|
if (insertId !== undefined && this.meta.pkFields.length > 0) {
|
|
111
117
|
const dbPkField = this.meta.pkFields[0];
|
|
@@ -152,7 +158,7 @@ export class TableDB {
|
|
|
152
158
|
.set(dbData)
|
|
153
159
|
.where(sql.join(conditions, sql ` AND `))
|
|
154
160
|
.execute();
|
|
155
|
-
const numUpdated = result[0]?.numUpdatedRows ?? 0n;
|
|
161
|
+
const numUpdated = result[0]?.numUpdatedRows ?? result[0]?.numAffectedRows ?? 0n;
|
|
156
162
|
if (Number(numUpdated) === 0) {
|
|
157
163
|
throw new RecordNotFoundError(this.meta.tableName, id);
|
|
158
164
|
}
|
|
@@ -314,9 +320,30 @@ export class TableDB {
|
|
|
314
320
|
.deleteFrom(this.meta.tableName)
|
|
315
321
|
.where(sql.join(conditions, sql ` AND `))
|
|
316
322
|
.execute();
|
|
317
|
-
const numDeleted = result[0]?.numDeletedRows ?? 0n;
|
|
323
|
+
const numDeleted = result[0]?.numDeletedRows ?? result[0]?.numAffectedRows ?? 0n;
|
|
318
324
|
return { deleted: Number(numDeleted) > 0 };
|
|
319
325
|
}
|
|
326
|
+
async deleteMany(where) {
|
|
327
|
+
const qb = this.db;
|
|
328
|
+
let query = qb.deleteFrom(this.meta.tableName);
|
|
329
|
+
const conditions = buildWhereConditions(where, this.meta);
|
|
330
|
+
if (conditions.length > 0) {
|
|
331
|
+
query = query.where(sql.join(conditions, sql ` AND `));
|
|
332
|
+
}
|
|
333
|
+
const result = await query.execute();
|
|
334
|
+
const numDeleted = result[0]?.numDeletedRows ?? result[0]?.numAffectedRows ?? 0n;
|
|
335
|
+
return { deleted: Number(numDeleted) };
|
|
336
|
+
}
|
|
337
|
+
async insertOrIgnore(data, ...args) {
|
|
338
|
+
const dbOnlyData = args[0];
|
|
339
|
+
return {
|
|
340
|
+
ids: () => this.insertIds(data, dbOnlyData, { onConflict: "ignore" }),
|
|
341
|
+
full: async () => {
|
|
342
|
+
const ids = await this.insertIds(data, dbOnlyData, { onConflict: "ignore" });
|
|
343
|
+
return this.reconcileIds(data, ids);
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
320
347
|
async count(where) {
|
|
321
348
|
const qb = this.db;
|
|
322
349
|
let query = qb
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createShapePlugin } from "./plugin.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createShapePlugin } from "./plugin.js";
|