web-idb-client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +230 -0
  3. package/dist/client/index.d.ts +7 -0
  4. package/dist/client/index.js +12 -0
  5. package/dist/client/index.js.map +1 -0
  6. package/dist/client/indexed-db-client.d.ts +40 -0
  7. package/dist/client/indexed-db-client.js +211 -0
  8. package/dist/client/indexed-db-client.js.map +1 -0
  9. package/dist/client/mutations.d.ts +12 -0
  10. package/dist/client/mutations.js +3 -0
  11. package/dist/client/mutations.js.map +1 -0
  12. package/dist/client/store.d.ts +42 -0
  13. package/dist/client/store.js +356 -0
  14. package/dist/client/store.js.map +1 -0
  15. package/dist/client/transaction.d.ts +14 -0
  16. package/dist/client/transaction.js +37 -0
  17. package/dist/client/transaction.js.map +1 -0
  18. package/dist/errors/index.d.ts +15 -0
  19. package/dist/errors/index.js +35 -0
  20. package/dist/errors/index.js.map +1 -0
  21. package/dist/index.d.ts +10 -0
  22. package/dist/index.js +33 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/liveQuery/index.d.ts +31 -0
  25. package/dist/liveQuery/index.js +216 -0
  26. package/dist/liveQuery/index.js.map +1 -0
  27. package/dist/migrate/diff.d.ts +28 -0
  28. package/dist/migrate/diff.js +89 -0
  29. package/dist/migrate/diff.js.map +1 -0
  30. package/dist/migrate/helpers.d.ts +29 -0
  31. package/dist/migrate/helpers.js +89 -0
  32. package/dist/migrate/helpers.js.map +1 -0
  33. package/dist/migrate/index.d.ts +6 -0
  34. package/dist/migrate/index.js +14 -0
  35. package/dist/migrate/index.js.map +1 -0
  36. package/dist/migrate/upgrade.d.ts +19 -0
  37. package/dist/migrate/upgrade.js +123 -0
  38. package/dist/migrate/upgrade.js.map +1 -0
  39. package/dist/query/order.d.ts +8 -0
  40. package/dist/query/order.js +30 -0
  41. package/dist/query/order.js.map +1 -0
  42. package/dist/query/where.d.ts +17 -0
  43. package/dist/query/where.js +106 -0
  44. package/dist/query/where.js.map +1 -0
  45. package/dist/relation/define-relation.d.ts +49 -0
  46. package/dist/relation/define-relation.js +101 -0
  47. package/dist/relation/define-relation.js.map +1 -0
  48. package/dist/relation/index.d.ts +3 -0
  49. package/dist/relation/index.js +14 -0
  50. package/dist/relation/index.js.map +1 -0
  51. package/dist/relation/load-related.d.ts +21 -0
  52. package/dist/relation/load-related.js +54 -0
  53. package/dist/relation/load-related.js.map +1 -0
  54. package/dist/schema/define-db.d.ts +5 -0
  55. package/dist/schema/define-db.js +41 -0
  56. package/dist/schema/define-db.js.map +1 -0
  57. package/dist/schema/define-store.d.ts +17 -0
  58. package/dist/schema/define-store.js +95 -0
  59. package/dist/schema/define-store.js.map +1 -0
  60. package/dist/schema/fields.d.ts +75 -0
  61. package/dist/schema/fields.js +90 -0
  62. package/dist/schema/fields.js.map +1 -0
  63. package/dist/schema/index.d.ts +5 -0
  64. package/dist/schema/index.js +22 -0
  65. package/dist/schema/index.js.map +1 -0
  66. package/dist/schema/infer.d.ts +131 -0
  67. package/dist/schema/infer.js +3 -0
  68. package/dist/schema/infer.js.map +1 -0
  69. package/dist/schema/infer.test-types.d.ts +11 -0
  70. package/dist/schema/infer.test-types.js +80 -0
  71. package/dist/schema/infer.test-types.js.map +1 -0
  72. package/dist/schema/types.d.ts +55 -0
  73. package/dist/schema/types.js +3 -0
  74. package/dist/schema/types.js.map +1 -0
  75. package/dist/utils/assert-keys.d.ts +3 -0
  76. package/dist/utils/assert-keys.js +15 -0
  77. package/dist/utils/assert-keys.js.map +1 -0
  78. package/dist/utils/boolean-codec.d.ts +23 -0
  79. package/dist/utils/boolean-codec.js +149 -0
  80. package/dist/utils/boolean-codec.js.map +1 -0
  81. package/dist/utils/index.d.ts +5 -0
  82. package/dist/utils/index.js +16 -0
  83. package/dist/utils/index.js.map +1 -0
  84. package/dist/utils/key-range.d.ts +14 -0
  85. package/dist/utils/key-range.js +157 -0
  86. package/dist/utils/key-range.js.map +1 -0
  87. package/dist/utils/promisify.d.ts +2 -0
  88. package/dist/utils/promisify.js +29 -0
  89. package/dist/utils/promisify.js.map +1 -0
  90. package/dist/utils/type-utils.d.ts +2 -0
  91. package/dist/utils/type-utils.js +3 -0
  92. package/dist/utils/type-utils.js.map +1 -0
  93. package/package.json +71 -0
@@ -0,0 +1,8 @@
1
+ import type { LooseOrderByInputInterface } from '../schema/infer';
2
+ import type { StoreInterface } from '../schema/types';
3
+ export interface ResolvedOrderInterface {
4
+ direction: IDBCursorDirection;
5
+ /** If set, iterate via this index; otherwise via PK */
6
+ indexName?: string;
7
+ }
8
+ export declare function resolveOrderBy(store: StoreInterface, orderBy?: LooseOrderByInputInterface): ResolvedOrderInterface | null;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveOrderBy = void 0;
4
+ const errors_1 = require("../errors");
5
+ function resolveOrderBy(store, orderBy) {
6
+ var _a;
7
+ if (!orderBy) {
8
+ return null;
9
+ }
10
+ const keys = Object.keys(orderBy);
11
+ if (keys.length === 0) {
12
+ return null;
13
+ }
14
+ if (keys.length > 1) {
15
+ throw new errors_1.IndexedDbError('QUERY_INVALID', 'orderBy supports a single field in v2', { store: store.name });
16
+ }
17
+ const field = keys[0];
18
+ const dir = (_a = orderBy[field]) !== null && _a !== void 0 ? _a : 'asc';
19
+ const direction = dir === 'desc' ? 'prev' : 'next';
20
+ if (field === store.primaryKey.keyPath) {
21
+ return { direction };
22
+ }
23
+ const index = store.indexes.find((i) => i.name === field || i.keyPath === field);
24
+ if (!index) {
25
+ throw new errors_1.IndexedDbError('QUERY_INVALID', `orderBy field "${field}" is not indexed on store "${store.name}"`, { store: store.name, field });
26
+ }
27
+ return { direction, indexName: index.name };
28
+ }
29
+ exports.resolveOrderBy = resolveOrderBy;
30
+ //# sourceMappingURL=order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order.js","sourceRoot":"","sources":["../../src/query/order.ts"],"names":[],"mappings":";;;AAEA,sCAA2C;AAQ3C,SAAgB,cAAc,CAC5B,KAAqB,EACrB,OAAoC;;IAEpC,IAAI,CAAC,OAAO,EAAE;QACZ,OAAO,IAAI,CAAC;KACb;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QACrB,OAAO,IAAI,CAAC;KACb;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACnB,MAAM,IAAI,uBAAc,CACtB,eAAe,EACf,uCAAuC,EACvC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CACtB,CAAC;KACH;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,GAAG,GAAmB,MAAA,OAAO,CAAC,KAAK,CAAC,mCAAI,KAAK,CAAC;IACpD,MAAM,SAAS,GAAuB,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAEvE,IAAI,KAAK,KAAK,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE;QACtC,OAAO,EAAE,SAAS,EAAE,CAAC;KACtB;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,CAC/C,CAAC;IACF,IAAI,CAAC,KAAK,EAAE;QACV,MAAM,IAAI,uBAAc,CACtB,eAAe,EACf,kBAAkB,KAAK,8BAA8B,KAAK,CAAC,IAAI,GAAG,EAClE,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAC7B,CAAC;KACH;IACD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;AAC9C,CAAC;AArCD,wCAqCC"}
@@ -0,0 +1,17 @@
1
+ import type { StoreInterface } from '../schema/types';
2
+ import type { LooseWhereInputInterface } from '../schema/infer';
3
+ export interface ResolvedQueryInterface {
4
+ /** Use object store directly (PK) vs named index */
5
+ source: 'store' | 'index';
6
+ indexName?: string;
7
+ keyRange?: IDBKeyRange | IDBValidKey;
8
+ }
9
+ /**
10
+ * Resolve a where object to a single index/store lookup.
11
+ * Multi-key where is only supported when it matches a compound index exactly.
12
+ * Named compound: `{ byUserDue: [userId, dueAt] }`.
13
+ */
14
+ export declare function resolveWhere(store: StoreInterface, where?: LooseWhereInputInterface): ResolvedQueryInterface;
15
+ export declare function isUniqueTarget(store: StoreInterface, where: LooseWhereInputInterface): boolean;
16
+ /** Whether where and orderBy would require different IDB sources. */
17
+ export declare function orderConflictsWithWhere(_store: StoreInterface, resolved: ResolvedQueryInterface, orderIndexName: string | undefined): boolean;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.orderConflictsWithWhere = exports.isUniqueTarget = exports.resolveWhere = void 0;
4
+ const key_range_1 = require("../utils/key-range");
5
+ const errors_1 = require("../errors");
6
+ function resolveIndexName(store, whereKey) {
7
+ if (whereKey === store.primaryKey.keyPath ||
8
+ (whereKey === 'id' && store.primaryKey.keyPath === 'id')) {
9
+ return { source: 'store' };
10
+ }
11
+ const byName = store.indexes.find((i) => i.name === whereKey);
12
+ if (byName) {
13
+ return { source: 'index', indexName: byName.name };
14
+ }
15
+ const byField = store.indexes.find((i) => typeof i.keyPath === 'string' && i.keyPath === whereKey);
16
+ if (byField) {
17
+ return { source: 'index', indexName: byField.name };
18
+ }
19
+ throw new errors_1.IndexedDbError('QUERY_INVALID', `Field or index "${whereKey}" is not indexed on store "${store.name}". Declare it with .index() / compoundIndex() in the schema.`, { store: store.name, field: whereKey });
20
+ }
21
+ function resolveNamedCompound(store, indexName, value) {
22
+ const compound = store.indexes.find((i) => i.name === indexName && Array.isArray(i.keyPath));
23
+ if (!compound || !Array.isArray(compound.keyPath)) {
24
+ return null;
25
+ }
26
+ if (!Array.isArray(value)) {
27
+ throw new errors_1.IndexedDbError('QUERY_INVALID', `Compound index "${indexName}" expects an array of key parts`, { store: store.name, index: indexName });
28
+ }
29
+ if (value.length !== compound.keyPath.length) {
30
+ throw new errors_1.IndexedDbError('QUERY_INVALID', `Compound index "${indexName}" expects ${compound.keyPath.length} key parts, got ${value.length}`, { store: store.name, index: indexName });
31
+ }
32
+ return {
33
+ source: 'index',
34
+ indexName: compound.name,
35
+ keyRange: (0, key_range_1.toCompoundKeyRange)(value),
36
+ };
37
+ }
38
+ /**
39
+ * Resolve a where object to a single index/store lookup.
40
+ * Multi-key where is only supported when it matches a compound index exactly.
41
+ * Named compound: `{ byUserDue: [userId, dueAt] }`.
42
+ */
43
+ function resolveWhere(store, where) {
44
+ if (!where || Object.keys(where).length === 0) {
45
+ return { source: 'store' };
46
+ }
47
+ const keys = Object.keys(where);
48
+ if (keys.length === 1) {
49
+ const key = keys[0];
50
+ const value = where[key];
51
+ const namedCompound = resolveNamedCompound(store, key, value);
52
+ if (namedCompound) {
53
+ return namedCompound;
54
+ }
55
+ const target = resolveIndexName(store, key);
56
+ return Object.assign(Object.assign({}, target), { keyRange: (0, key_range_1.toKeyRange)(value) });
57
+ }
58
+ // Compound: prefer an index whose keyPath matches all keys (order-sensitive)
59
+ const compound = store.indexes.find((i) => Array.isArray(i.keyPath) &&
60
+ i.keyPath.length === keys.length &&
61
+ i.keyPath.every((k) => keys.includes(k)));
62
+ if (compound && Array.isArray(compound.keyPath)) {
63
+ const parts = compound.keyPath.map((k) => {
64
+ const v = where[k];
65
+ if (v === undefined) {
66
+ throw new errors_1.IndexedDbError('QUERY_INVALID', `Missing compound key part "${k}"`, { store: store.name, index: compound.name, field: k });
67
+ }
68
+ return v;
69
+ });
70
+ return {
71
+ source: 'index',
72
+ indexName: compound.name,
73
+ keyRange: (0, key_range_1.toCompoundKeyRange)(parts),
74
+ };
75
+ }
76
+ throw new errors_1.IndexedDbError('QUERY_INVALID', `Cannot resolve where keys [${keys.join(', ')}] to a single index on "${store.name}". Use one indexed field or a declared compound index.`, { store: store.name });
77
+ }
78
+ exports.resolveWhere = resolveWhere;
79
+ function isUniqueTarget(store, where) {
80
+ const keys = Object.keys(where);
81
+ if (keys.length !== 1) {
82
+ return false;
83
+ }
84
+ const key = keys[0];
85
+ if (key === store.primaryKey.keyPath) {
86
+ return true;
87
+ }
88
+ const index = store.indexes.find((i) => i.name === key || i.keyPath === key);
89
+ return Boolean(index === null || index === void 0 ? void 0 : index.unique);
90
+ }
91
+ exports.isUniqueTarget = isUniqueTarget;
92
+ /** Whether where and orderBy would require different IDB sources. */
93
+ function orderConflictsWithWhere(_store, resolved, orderIndexName) {
94
+ if (!orderIndexName) {
95
+ // orderBy PK: conflicts when where uses a secondary index
96
+ return resolved.source === 'index' && Boolean(resolved.indexName);
97
+ }
98
+ if (resolved.source === 'store') {
99
+ // No where / empty where → iterate the orderBy index freely.
100
+ // PK keyRange where + secondary orderBy → needs sortInMemory.
101
+ return resolved.keyRange !== undefined;
102
+ }
103
+ return Boolean(resolved.indexName && resolved.indexName !== orderIndexName);
104
+ }
105
+ exports.orderConflictsWithWhere = orderConflictsWithWhere;
106
+ //# sourceMappingURL=where.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"where.js","sourceRoot":"","sources":["../../src/query/where.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { DbStoresInterface, StoreInterface } from '../schema/types';
2
+ export type RelationType = 'hasMany' | 'belongsTo';
3
+ export interface RelationEndpoint {
4
+ store: string;
5
+ /** FK or PK field name on that store */
6
+ field: string;
7
+ }
8
+ /**
9
+ * Declarative relation metadata for docs / validation.
10
+ * Does **not** enable Prisma-style `include` — use `loadMany` / `loadOne`.
11
+ */
12
+ export interface RelationInterface {
13
+ name: string;
14
+ type: RelationType;
15
+ /**
16
+ * hasMany: parent side (PK). belongsTo: child side (FK field on this store).
17
+ */
18
+ from: RelationEndpoint;
19
+ /**
20
+ * hasMany: child side (FK field). belongsTo: parent side (PK).
21
+ */
22
+ to: RelationEndpoint;
23
+ }
24
+ /**
25
+ * Typed relation endpoint from a store + field key (avoids store/field typos).
26
+ *
27
+ * ```ts
28
+ * relation({
29
+ * name: 'tasksOfUser',
30
+ * type: 'hasMany',
31
+ * from: end(users, 'id'),
32
+ * to: end(tasks, 'userId'),
33
+ * })
34
+ * ```
35
+ */
36
+ export declare function end<TStore extends StoreInterface, TField extends keyof TStore['fields'] & string>(store: TStore, field: TField): RelationEndpoint;
37
+ export declare function relation(def: RelationInterface): RelationInterface;
38
+ /**
39
+ * Ensure every `.ref()` field is indexed (or is the PK), and that the
40
+ * target store exists when `stores` is provided.
41
+ */
42
+ export declare function assertRefsValid(store: StoreInterface, stores?: DbStoresInterface): void;
43
+ /**
44
+ * Validate a `relation()` against store map: endpoints exist and the FK
45
+ * side is indexed.
46
+ */
47
+ export declare function assertRelationValid(rel: RelationInterface, stores: DbStoresInterface): void;
48
+ /** Validate all `.ref()` fields across a client store map. */
49
+ export declare function assertAllRefsValid(stores: DbStoresInterface): void;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertAllRefsValid = exports.assertRelationValid = exports.assertRefsValid = exports.relation = exports.end = void 0;
4
+ const errors_1 = require("../errors");
5
+ /**
6
+ * Typed relation endpoint from a store + field key (avoids store/field typos).
7
+ *
8
+ * ```ts
9
+ * relation({
10
+ * name: 'tasksOfUser',
11
+ * type: 'hasMany',
12
+ * from: end(users, 'id'),
13
+ * to: end(tasks, 'userId'),
14
+ * })
15
+ * ```
16
+ */
17
+ function end(store, field) {
18
+ if (!(field in store.fields)) {
19
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `end(): store "${store.name}" has no field "${field}"`, { store: store.name, field });
20
+ }
21
+ return { store: store.name, field };
22
+ }
23
+ exports.end = end;
24
+ function relation(def) {
25
+ if (!def.name) {
26
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', 'relation() requires a name');
27
+ }
28
+ if (def.type !== 'hasMany' && def.type !== 'belongsTo') {
29
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `relation "${def.name}": type must be "hasMany" or "belongsTo"`);
30
+ }
31
+ return def;
32
+ }
33
+ exports.relation = relation;
34
+ function isQueryableField(store, fieldName) {
35
+ if (store.primaryKey.keyPath === fieldName) {
36
+ return true;
37
+ }
38
+ return store.indexes.some((i) => i.name === fieldName ||
39
+ (typeof i.keyPath === 'string' && i.keyPath === fieldName));
40
+ }
41
+ /**
42
+ * Ensure every `.ref()` field is indexed (or is the PK), and that the
43
+ * target store exists when `stores` is provided.
44
+ */
45
+ function assertRefsValid(store, stores) {
46
+ for (const [fieldName, fieldDef] of Object.entries(store.fields)) {
47
+ const ref = fieldDef.meta.ref;
48
+ if (!ref) {
49
+ continue;
50
+ }
51
+ if (!isQueryableField(store, fieldName)) {
52
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `Store "${store.name}" field "${fieldName}" has .ref("${ref.store}") but is not indexed. Chain .index() / .uniqueIndex() so FK lookups use an IDB index.`, { store: store.name, field: fieldName });
53
+ }
54
+ if (stores) {
55
+ const byName = new Map(Object.values(stores).map((s) => [s.name, s]));
56
+ if (!byName.has(ref.store)) {
57
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `Store "${store.name}" field "${fieldName}" refs unknown store "${ref.store}". Known: ${[...byName.keys()].join(', ') || '(none)'}`, { store: store.name, field: fieldName });
58
+ }
59
+ }
60
+ }
61
+ }
62
+ exports.assertRefsValid = assertRefsValid;
63
+ /**
64
+ * Validate a `relation()` against store map: endpoints exist and the FK
65
+ * side is indexed.
66
+ */
67
+ function assertRelationValid(rel, stores) {
68
+ const byName = new Map(Object.values(stores).map((s) => [s.name, s]));
69
+ const fromStore = byName.get(rel.from.store);
70
+ const toStore = byName.get(rel.to.store);
71
+ if (!fromStore) {
72
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `relation "${rel.name}": unknown from.store "${rel.from.store}"`);
73
+ }
74
+ if (!toStore) {
75
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `relation "${rel.name}": unknown to.store "${rel.to.store}"`);
76
+ }
77
+ if (!(rel.from.field in fromStore.fields)) {
78
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `relation "${rel.name}": from.field "${rel.from.field}" missing on "${rel.from.store}"`, { store: rel.from.store, field: rel.from.field });
79
+ }
80
+ if (!(rel.to.field in toStore.fields)) {
81
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `relation "${rel.name}": to.field "${rel.to.field}" missing on "${rel.to.store}"`, { store: rel.to.store, field: rel.to.field });
82
+ }
83
+ // FK must be indexed: hasMany → to.field on child; belongsTo → from.field on child
84
+ if (rel.type === 'hasMany') {
85
+ if (!isQueryableField(toStore, rel.to.field)) {
86
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `relation "${rel.name}" (hasMany): FK "${rel.to.store}.${rel.to.field}" must be indexed`, { store: rel.to.store, field: rel.to.field });
87
+ }
88
+ }
89
+ else if (!isQueryableField(fromStore, rel.from.field)) {
90
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `relation "${rel.name}" (belongsTo): FK "${rel.from.store}.${rel.from.field}" must be indexed`, { store: rel.from.store, field: rel.from.field });
91
+ }
92
+ }
93
+ exports.assertRelationValid = assertRelationValid;
94
+ /** Validate all `.ref()` fields across a client store map. */
95
+ function assertAllRefsValid(stores) {
96
+ for (const store of Object.values(stores)) {
97
+ assertRefsValid(store, stores);
98
+ }
99
+ }
100
+ exports.assertAllRefsValid = assertAllRefsValid;
101
+ //# sourceMappingURL=define-relation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-relation.js","sourceRoot":"","sources":["../../src/relation/define-relation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { relation, end, assertRefsValid, assertRelationValid, assertAllRefsValid, } from './define-relation';
2
+ export type { RelationInterface, RelationType, RelationEndpoint, } from './define-relation';
3
+ export { loadMany, loadOne, findByForeignKey } from './load-related';
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findByForeignKey = exports.loadOne = exports.loadMany = exports.assertAllRefsValid = exports.assertRelationValid = exports.assertRefsValid = exports.end = exports.relation = void 0;
4
+ var define_relation_1 = require("./define-relation");
5
+ Object.defineProperty(exports, "relation", { enumerable: true, get: function () { return define_relation_1.relation; } });
6
+ Object.defineProperty(exports, "end", { enumerable: true, get: function () { return define_relation_1.end; } });
7
+ Object.defineProperty(exports, "assertRefsValid", { enumerable: true, get: function () { return define_relation_1.assertRefsValid; } });
8
+ Object.defineProperty(exports, "assertRelationValid", { enumerable: true, get: function () { return define_relation_1.assertRelationValid; } });
9
+ Object.defineProperty(exports, "assertAllRefsValid", { enumerable: true, get: function () { return define_relation_1.assertAllRefsValid; } });
10
+ var load_related_1 = require("./load-related");
11
+ Object.defineProperty(exports, "loadMany", { enumerable: true, get: function () { return load_related_1.loadMany; } });
12
+ Object.defineProperty(exports, "loadOne", { enumerable: true, get: function () { return load_related_1.loadOne; } });
13
+ Object.defineProperty(exports, "findByForeignKey", { enumerable: true, get: function () { return load_related_1.findByForeignKey; } });
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/relation/index.ts"],"names":[],"mappings":";;;AAAA,qDAM2B;AALzB,2GAAA,QAAQ,OAAA;AACR,sGAAA,GAAG,OAAA;AACH,kHAAA,eAAe,OAAA;AACf,sHAAA,mBAAmB,OAAA;AACnB,qHAAA,kBAAkB,OAAA;AAOpB,+CAAqE;AAA5D,wGAAA,QAAQ,OAAA;AAAE,uGAAA,OAAO,OAAA;AAAE,gHAAA,gBAAgB,OAAA"}
@@ -0,0 +1,21 @@
1
+ import type { Store } from '../client/store';
2
+ import type { StoreInterface } from '../schema/types';
3
+ import type { InferSelect } from '../schema/infer';
4
+ /**
5
+ * Load many child rows by foreign key (`hasMany`).
6
+ *
7
+ * Explicit helper — not Prisma `include`. Prefer calling inside
8
+ * `transaction()` when loading parent + children together.
9
+ *
10
+ * **N+1:** calling this once per parent in a loop hits the index once per
11
+ * parent. For many parents, prefer one `findMany` + group in memory, or
12
+ * accept the N+1 cost for small sets.
13
+ */
14
+ export declare function loadMany<TStore extends StoreInterface>(childStore: Store<TStore>, foreignKey: keyof TStore['fields'] & string, parentKey: IDBValidKey): Promise<InferSelect<TStore>[]>;
15
+ /**
16
+ * Load one parent row by primary key (`belongsTo`).
17
+ * Pass the FK value from the child row.
18
+ */
19
+ export declare function loadOne<TStore extends StoreInterface>(parentStore: Store<TStore>, primaryKey: IDBValidKey): Promise<InferSelect<TStore> | null>;
20
+ /** Alias of `loadMany` for readability at call sites. */
21
+ export declare const findByForeignKey: typeof loadMany;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.findByForeignKey = exports.loadOne = exports.loadMany = void 0;
13
+ const errors_1 = require("../errors");
14
+ /**
15
+ * Load many child rows by foreign key (`hasMany`).
16
+ *
17
+ * Explicit helper — not Prisma `include`. Prefer calling inside
18
+ * `transaction()` when loading parent + children together.
19
+ *
20
+ * **N+1:** calling this once per parent in a loop hits the index once per
21
+ * parent. For many parents, prefer one `findMany` + group in memory, or
22
+ * accept the N+1 cost for small sets.
23
+ */
24
+ function loadMany(childStore, foreignKey, parentKey) {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ const field = childStore.def.fields[foreignKey];
27
+ if (!field) {
28
+ throw new errors_1.IndexedDbError('QUERY_INVALID', `loadMany: unknown field "${foreignKey}" on store "${childStore.name}"`, { store: childStore.name, field: foreignKey });
29
+ }
30
+ if (!field.meta.index && !field.meta.primaryKey) {
31
+ throw new errors_1.IndexedDbError('QUERY_INVALID', `loadMany: "${childStore.name}.${foreignKey}" must be indexed for FK lookup`, { store: childStore.name, field: foreignKey });
32
+ }
33
+ return childStore.findMany({
34
+ where: { [foreignKey]: parentKey },
35
+ });
36
+ });
37
+ }
38
+ exports.loadMany = loadMany;
39
+ /**
40
+ * Load one parent row by primary key (`belongsTo`).
41
+ * Pass the FK value from the child row.
42
+ */
43
+ function loadOne(parentStore, primaryKey) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ const pk = parentStore.def.primaryKey.keyPath;
46
+ return parentStore.find({
47
+ where: { [pk]: primaryKey },
48
+ });
49
+ });
50
+ }
51
+ exports.loadOne = loadOne;
52
+ /** Alias of `loadMany` for readability at call sites. */
53
+ exports.findByForeignKey = loadMany;
54
+ //# sourceMappingURL=load-related.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-related.js","sourceRoot":"","sources":["../../src/relation/load-related.ts"],"names":[],"mappings":";;;;;;;;;;;;AAGA,sCAA2C;AAE3C;;;;;;;;;GASG;AACH,SAAsB,QAAQ,CAC5B,UAAyB,EACzB,UAA2C,EAC3C,SAAsB;;QAEtB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,EAAE;YACV,MAAM,IAAI,uBAAc,CACtB,eAAe,EACf,4BAA4B,UAAU,eAAe,UAAU,CAAC,IAAI,GAAG,EACvE,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAC9C,CAAC;SACH;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE;YAC/C,MAAM,IAAI,uBAAc,CACtB,eAAe,EACf,cAAc,UAAU,CAAC,IAAI,IAAI,UAAU,iCAAiC,EAC5E,EAAE,KAAK,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAC9C,CAAC;SACH;QACD,OAAO,UAAU,CAAC,QAAQ,CAAC;YACzB,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAW;SAC5C,CAAC,CAAC;IACL,CAAC;CAAA;AAvBD,4BAuBC;AAED;;;GAGG;AACH,SAAsB,OAAO,CAC3B,WAA0B,EAC1B,UAAuB;;QAEvB,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC;QAC9C,OAAO,WAAW,CAAC,IAAI,CAAC;YACtB,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,UAAU,EAAW;SACrC,CAAC,CAAC;IACL,CAAC;CAAA;AARD,0BAQC;AAED,yDAAyD;AAC5C,QAAA,gBAAgB,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { DbConfigInterface, DbStoresInterface, DeclaredDbInterface, DeclaredStoreInterface, StoreInterface } from './types';
2
+ /** Normalize store defs into a serializable schema snapshot for migrations. */
3
+ export declare function toDeclaredStoreSchema(store: StoreInterface): DeclaredStoreInterface;
4
+ export declare function toDeclaredDbSchema<TStores extends DbStoresInterface>(config: DbConfigInterface<TStores>): DeclaredDbInterface;
5
+ export type { DbConfigInterface };
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toDeclaredDbSchema = exports.toDeclaredStoreSchema = void 0;
4
+ const errors_1 = require("../errors");
5
+ const define_relation_1 = require("../relation/define-relation");
6
+ /** Normalize store defs into a serializable schema snapshot for migrations. */
7
+ function toDeclaredStoreSchema(store) {
8
+ return {
9
+ name: store.name,
10
+ primaryKey: Object.assign({}, store.primaryKey),
11
+ indexes: store.indexes.map((index) => (Object.assign({}, index))),
12
+ };
13
+ }
14
+ exports.toDeclaredStoreSchema = toDeclaredStoreSchema;
15
+ function toDeclaredDbSchema(config) {
16
+ if (!config.name) {
17
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', 'Database name is required');
18
+ }
19
+ if (!Number.isInteger(config.version) || config.version < 1) {
20
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', 'Database version must be an integer >= 1');
21
+ }
22
+ const stores = Object.values(config.stores).map(toDeclaredStoreSchema);
23
+ if (stores.length === 0) {
24
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', 'At least one store is required');
25
+ }
26
+ const names = new Set();
27
+ for (const store of stores) {
28
+ if (names.has(store.name)) {
29
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `Duplicate store name "${store.name}"`);
30
+ }
31
+ names.add(store.name);
32
+ }
33
+ (0, define_relation_1.assertAllRefsValid)(config.stores);
34
+ return {
35
+ name: config.name,
36
+ version: config.version,
37
+ stores,
38
+ };
39
+ }
40
+ exports.toDeclaredDbSchema = toDeclaredDbSchema;
41
+ //# sourceMappingURL=define-db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-db.js","sourceRoot":"","sources":["../../src/schema/define-db.ts"],"names":[],"mappings":";;;AAOA,sCAA2C;AAC3C,iEAAiE;AAEjE,+EAA+E;AAC/E,SAAgB,qBAAqB,CACnC,KAAqB;IAErB,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,UAAU,oBAAO,KAAK,CAAC,UAAU,CAAE;QACnC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAM,KAAK,EAAG,CAAC;KACtD,CAAC;AACJ,CAAC;AARD,sDAQC;AAED,SAAgB,kBAAkB,CAChC,MAAkC;IAElC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;QAChB,MAAM,IAAI,uBAAc,CAAC,gBAAgB,EAAE,2BAA2B,CAAC,CAAC;KACzE;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE;QAC3D,MAAM,IAAI,uBAAc,CACtB,gBAAgB,EAChB,0CAA0C,CAC3C,CAAC;KACH;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACvE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;QACvB,MAAM,IAAI,uBAAc,CAAC,gBAAgB,EAAE,gCAAgC,CAAC,CAAC;KAC9E;IACD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;QAC1B,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;YACzB,MAAM,IAAI,uBAAc,CACtB,gBAAgB,EAChB,yBAAyB,KAAK,CAAC,IAAI,GAAG,CACvC,CAAC;SACH;QACD,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;KACvB;IACD,IAAA,oCAAkB,EAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM;KACP,CAAC;AACJ,CAAC;AAhCD,gDAgCC"}
@@ -0,0 +1,17 @@
1
+ import { type AnyField } from './fields';
2
+ import type { CompoundIndexType, StoreFieldsInterface, StoreInterface } from './types';
3
+ export type CompoundIndexInterface<TFieldNames extends readonly string[] = readonly string[]> = {
4
+ name: string;
5
+ fields: TFieldNames;
6
+ unique?: boolean;
7
+ };
8
+ /** Declare a compound (multi-property) index. */
9
+ export declare function compoundIndex<const TFieldNames extends readonly string[]>(name: string, fields: TFieldNames, options?: {
10
+ unique?: boolean;
11
+ }): CompoundIndexInterface<TFieldNames>;
12
+ /**
13
+ * Define an object store schema. Only indexed fields become IDBIndex entries;
14
+ * all fields participate in TypeScript InferSelect / InferInsert / typed where.
15
+ */
16
+ export declare function defineStore<TFields extends StoreFieldsInterface, const TCompounds extends readonly CompoundIndexType[] = readonly CompoundIndexType[]>(name: string, fields: TFields, indexes?: (fields: TFields) => TCompounds): StoreInterface<TFields, TCompounds>;
17
+ export type { AnyField };
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineStore = exports.compoundIndex = void 0;
4
+ const fields_1 = require("./fields");
5
+ const errors_1 = require("../errors");
6
+ /** Declare a compound (multi-property) index. */
7
+ function compoundIndex(name, fields, options) {
8
+ return { name, fields, unique: options === null || options === void 0 ? void 0 : options.unique };
9
+ }
10
+ exports.compoundIndex = compoundIndex;
11
+ function resolvePrimaryKey(fields) {
12
+ const entries = Object.entries(fields).filter(([, fieldDef]) => fieldDef.meta.primaryKey);
13
+ if (entries.length === 0) {
14
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', 'Store must declare exactly one primaryKey() field');
15
+ }
16
+ if (entries.length > 1) {
17
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', 'Store must declare only one primaryKey() field');
18
+ }
19
+ const [keyPath, fieldDef] = entries[0];
20
+ return {
21
+ keyPath,
22
+ autoIncrement: fieldDef.meta.autoIncrement,
23
+ };
24
+ }
25
+ function collectIndexes(fields, extra = []) {
26
+ const indexes = [];
27
+ const names = new Set();
28
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
29
+ const spec = (0, fields_1.fieldIndexSpec)(fieldName, fieldDef);
30
+ if (!spec) {
31
+ continue;
32
+ }
33
+ if (names.has(spec.name)) {
34
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `Duplicate index name "${spec.name}"`);
35
+ }
36
+ names.add(spec.name);
37
+ indexes.push(spec);
38
+ }
39
+ for (const compound of extra) {
40
+ if (names.has(compound.name)) {
41
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `Duplicate index name "${compound.name}"`);
42
+ }
43
+ for (const fieldName of compound.fields) {
44
+ if (!(fieldName in fields) || !(0, fields_1.isField)(fields[fieldName])) {
45
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `Compound index "${compound.name}" references unknown field "${fieldName}"`);
46
+ }
47
+ }
48
+ names.add(compound.name);
49
+ indexes.push({
50
+ name: compound.name,
51
+ keyPath: [...compound.fields],
52
+ unique: compound.unique,
53
+ multiEntry: false,
54
+ });
55
+ }
56
+ return indexes;
57
+ }
58
+ /**
59
+ * Define an object store schema. Only indexed fields become IDBIndex entries;
60
+ * all fields participate in TypeScript InferSelect / InferInsert / typed where.
61
+ */
62
+ function defineStore(name, fields, indexes) {
63
+ if (!name) {
64
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', 'Store name is required');
65
+ }
66
+ for (const [key, value] of Object.entries(fields)) {
67
+ if (!(0, fields_1.isField)(value)) {
68
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `Store "${name}" field "${key}" must be created with field.* builders`);
69
+ }
70
+ }
71
+ const primaryKey = resolvePrimaryKey(fields);
72
+ const extra = (indexes ? indexes(fields) : []);
73
+ const indexList = collectIndexes(fields, [...extra]);
74
+ const store = {
75
+ name,
76
+ fields,
77
+ primaryKey,
78
+ indexes: indexList,
79
+ };
80
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
81
+ if (!fieldDef.meta.ref) {
82
+ continue;
83
+ }
84
+ const indexed = fieldDef.meta.primaryKey ||
85
+ Boolean(fieldDef.meta.index) ||
86
+ indexList.some((i) => i.name === fieldName ||
87
+ (typeof i.keyPath === 'string' && i.keyPath === fieldName));
88
+ if (!indexed) {
89
+ throw new errors_1.IndexedDbError('SCHEMA_INVALID', `Store "${name}" field "${fieldName}" has .ref("${fieldDef.meta.ref.store}") but is not indexed. Chain .index() / .uniqueIndex() so FK lookups use an IDB index.`, { store: name, field: fieldName });
90
+ }
91
+ }
92
+ return store;
93
+ }
94
+ exports.defineStore = defineStore;
95
+ //# sourceMappingURL=define-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-store.js","sourceRoot":"","sources":["../../src/schema/define-store.ts"],"names":[],"mappings":"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"}