@livestore/livestore 0.3.2-dev.9 → 0.4.0-dev.1
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/.tsbuildinfo +1 -1
- package/dist/SqliteDbWrapper.d.ts +5 -5
- package/dist/SqliteDbWrapper.d.ts.map +1 -1
- package/dist/SqliteDbWrapper.js.map +1 -1
- package/dist/live-queries/base-class.d.ts +5 -0
- package/dist/live-queries/base-class.d.ts.map +1 -1
- package/dist/live-queries/base-class.js +1 -1
- package/dist/live-queries/base-class.js.map +1 -1
- package/dist/live-queries/computed.d.ts.map +1 -1
- package/dist/live-queries/computed.js +7 -0
- package/dist/live-queries/computed.js.map +1 -1
- package/dist/live-queries/db-query.d.ts.map +1 -1
- package/dist/live-queries/db-query.js +12 -2
- package/dist/live-queries/db-query.js.map +1 -1
- package/dist/live-queries/signal.d.ts.map +1 -1
- package/dist/live-queries/signal.js +7 -0
- package/dist/live-queries/signal.js.map +1 -1
- package/dist/mod.d.ts +15 -56
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +1 -0
- package/dist/mod.js.map +1 -1
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +2 -0
- package/dist/reactive.js.map +1 -1
- package/dist/store/create-store.d.ts +5 -7
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/create-store.js +4 -4
- package/dist/store/create-store.js.map +1 -1
- package/dist/store/store-shutdown.test.d.ts +2 -0
- package/dist/store/store-shutdown.test.d.ts.map +1 -0
- package/dist/store/store-shutdown.test.js +103 -0
- package/dist/store/store-shutdown.test.js.map +1 -0
- package/dist/store/store-types.d.ts +4 -4
- package/dist/store/store-types.d.ts.map +1 -1
- package/dist/store/store-types.js.map +1 -1
- package/dist/store/store.d.ts +11 -2
- package/dist/store/store.d.ts.map +1 -1
- package/dist/store/store.js +76 -43
- package/dist/store/store.js.map +1 -1
- package/dist/utils/dev.d.ts +1 -1
- package/dist/utils/dev.d.ts.map +1 -1
- package/dist/utils/dev.js +14 -8
- package/dist/utils/dev.js.map +1 -1
- package/dist/utils/tests/fixture.d.ts +12 -0
- package/dist/utils/tests/fixture.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/SqliteDbWrapper.ts +4 -4
- package/src/live-queries/base-class.ts +5 -1
- package/src/live-queries/computed.ts +7 -0
- package/src/live-queries/db-query.ts +12 -3
- package/src/live-queries/signal.ts +7 -0
- package/src/mod.ts +2 -1
- package/src/reactive.ts +2 -0
- package/src/store/create-store.ts +18 -16
- package/src/store/store-types.ts +9 -5
- package/src/store/store.ts +68 -9
- package/src/utils/dev.ts +15 -8
package/dist/utils/dev.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { prettyBytes } from '@livestore/utils';
|
2
2
|
import { Effect } from '@livestore/utils/effect';
|
3
3
|
export const downloadBlob = (data, fileName, mimeType = 'application/octet-stream') => {
|
4
4
|
const blob = data instanceof Blob ? data : new Blob([data], { type: mimeType });
|
@@ -16,12 +16,18 @@ export const downloadURL = (data, fileName) => {
|
|
16
16
|
a.remove();
|
17
17
|
};
|
18
18
|
export const exposeDebugUtils = () => {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
globalThis.__debugLiveStoreUtils = {
|
20
|
+
downloadBlob,
|
21
|
+
runSync: (effect) => Effect.runSync(effect),
|
22
|
+
runFork: (effect) => Effect.runFork(effect),
|
23
|
+
dumpDb: (db) => {
|
24
|
+
const tables = db.select(`SELECT name FROM sqlite_master WHERE type='table'`);
|
25
|
+
for (const table of tables) {
|
26
|
+
const rows = db.select(`SELECT * FROM ${table.name}`);
|
27
|
+
console.log(`Table: ${table.name} (${prettyBytes(table.name.length)}, ${rows.length} rows)`);
|
28
|
+
console.table(rows);
|
29
|
+
}
|
30
|
+
},
|
31
|
+
};
|
26
32
|
};
|
27
33
|
//# sourceMappingURL=dev.js.map
|
package/dist/utils/dev.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/utils/dev.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/utils/dev.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhD,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,IAA6C,EAC7C,QAAgB,EAChB,QAAQ,GAAG,0BAA0B,EACrC,EAAE;IACF,MAAM,IAAI,GAAG,IAAI,YAAY,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IAE/E,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;IAE5C,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAE1B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAA;AACzD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,QAAgB,EAAE,EAAE;IAC5D,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA;IACrC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAA;IACb,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IACvB,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAA;IACxB,CAAC,CAAC,KAAK,EAAE,CAAA;IACT,CAAC,CAAC,MAAM,EAAE,CAAA;AACZ,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,EAAE;IACnC,UAAU,CAAC,qBAAqB,GAAG;QACjC,YAAY;QACZ,OAAO,EAAE,CAAC,MAAsC,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC3E,OAAO,EAAE,CAAC,MAAsC,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC3E,MAAM,EAAE,CAAC,EAAY,EAAE,EAAE;YACvB,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAmB,mDAAmD,CAAC,CAAA;YAC/F,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,CAAM,iBAAiB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;gBAC1D,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,QAAQ,CAAC,CAAA;gBAC5F,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACrB,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
@@ -18,6 +18,7 @@ export declare const todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefFor
|
|
18
18
|
default: import("effect/Option").None<never>;
|
19
19
|
nullable: false;
|
20
20
|
primaryKey: true;
|
21
|
+
autoIncrement: false;
|
21
22
|
};
|
22
23
|
readonly text: {
|
23
24
|
columnType: "text";
|
@@ -25,6 +26,7 @@ export declare const todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefFor
|
|
25
26
|
default: import("effect/Option").Some<"">;
|
26
27
|
nullable: false;
|
27
28
|
primaryKey: false;
|
29
|
+
autoIncrement: false;
|
28
30
|
};
|
29
31
|
readonly completed: {
|
30
32
|
columnType: "integer";
|
@@ -32,6 +34,7 @@ export declare const todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefFor
|
|
32
34
|
default: import("effect/Option").Some<false>;
|
33
35
|
nullable: false;
|
34
36
|
primaryKey: false;
|
37
|
+
autoIncrement: false;
|
35
38
|
};
|
36
39
|
}>, State.SQLite.WithDefaults<{
|
37
40
|
readonly id: {
|
@@ -40,6 +43,7 @@ export declare const todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefFor
|
|
40
43
|
default: import("effect/Option").None<never>;
|
41
44
|
nullable: false;
|
42
45
|
primaryKey: true;
|
46
|
+
autoIncrement: false;
|
43
47
|
};
|
44
48
|
readonly text: {
|
45
49
|
columnType: "text";
|
@@ -47,6 +51,7 @@ export declare const todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefFor
|
|
47
51
|
default: import("effect/Option").Some<"">;
|
48
52
|
nullable: false;
|
49
53
|
primaryKey: false;
|
54
|
+
autoIncrement: false;
|
50
55
|
};
|
51
56
|
readonly completed: {
|
52
57
|
columnType: "integer";
|
@@ -54,6 +59,7 @@ export declare const todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefFor
|
|
54
59
|
default: import("effect/Option").Some<false>;
|
55
60
|
nullable: false;
|
56
61
|
primaryKey: false;
|
62
|
+
autoIncrement: false;
|
57
63
|
};
|
58
64
|
}>, Schema.Schema<{
|
59
65
|
readonly id: string;
|
@@ -88,6 +94,7 @@ export declare const tables: {
|
|
88
94
|
default: import("effect/Option").None<never>;
|
89
95
|
nullable: false;
|
90
96
|
primaryKey: true;
|
97
|
+
autoIncrement: false;
|
91
98
|
};
|
92
99
|
readonly text: {
|
93
100
|
columnType: "text";
|
@@ -95,6 +102,7 @@ export declare const tables: {
|
|
95
102
|
default: import("effect/Option").Some<"">;
|
96
103
|
nullable: false;
|
97
104
|
primaryKey: false;
|
105
|
+
autoIncrement: false;
|
98
106
|
};
|
99
107
|
readonly completed: {
|
100
108
|
columnType: "integer";
|
@@ -102,6 +110,7 @@ export declare const tables: {
|
|
102
110
|
default: import("effect/Option").Some<false>;
|
103
111
|
nullable: false;
|
104
112
|
primaryKey: false;
|
113
|
+
autoIncrement: false;
|
105
114
|
};
|
106
115
|
}>, State.SQLite.WithDefaults<{
|
107
116
|
readonly id: {
|
@@ -110,6 +119,7 @@ export declare const tables: {
|
|
110
119
|
default: import("effect/Option").None<never>;
|
111
120
|
nullable: false;
|
112
121
|
primaryKey: true;
|
122
|
+
autoIncrement: false;
|
113
123
|
};
|
114
124
|
readonly text: {
|
115
125
|
columnType: "text";
|
@@ -117,6 +127,7 @@ export declare const tables: {
|
|
117
127
|
default: import("effect/Option").Some<"">;
|
118
128
|
nullable: false;
|
119
129
|
primaryKey: false;
|
130
|
+
autoIncrement: false;
|
120
131
|
};
|
121
132
|
readonly completed: {
|
122
133
|
columnType: "integer";
|
@@ -124,6 +135,7 @@ export declare const tables: {
|
|
124
135
|
default: import("effect/Option").Some<false>;
|
125
136
|
nullable: false;
|
126
137
|
primaryKey: false;
|
138
|
+
autoIncrement: false;
|
127
139
|
};
|
128
140
|
}>, Schema.Schema<{
|
129
141
|
readonly id: string;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../../src/utils/tests/fixture.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmC,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,KAAK,IAAI,MAAM,oBAAoB,CAAA;AAE/C,MAAM,MAAM,IAAI,GAAG;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEnD,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,KAAK
|
1
|
+
{"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../../src/utils/tests/fixture.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmC,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,KAAK,IAAI,MAAM,oBAAoB,CAAA;AAE/C,MAAM,MAAM,IAAI,GAAG;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEnD,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAOhB,CAAA;AAEF,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;EAOd,CAAA;AAEF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAiB,CAAA;AAEpC,eAAO,MAAM,MAAM;;;;;;;;;;CASlB,CAAA;AAMD,eAAO,MAAM,KAAK,8CAAoD,CAAA;AACtE,eAAO,MAAM,MAAM;;;;;;;;;;;;;EAAgC,CAAA;AAEnD,eAAO,MAAM,WAAW,GAAI,+BAGzB;IACD,UAAU,CAAC,EAAE,IAAI,CAAC,MAAM,CAAA;IACxB,WAAW,CAAC,EAAE,IAAI,CAAC,OAAO,CAAA;CACtB;;;;;;;;;;;;;mFAU4E,CAAA"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@livestore/livestore",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.4.0-dev.1",
|
4
4
|
"type": "module",
|
5
5
|
"sideEffects": false,
|
6
6
|
"exports": {
|
@@ -11,17 +11,17 @@
|
|
11
11
|
},
|
12
12
|
"dependencies": {
|
13
13
|
"@opentelemetry/api": "1.9.0",
|
14
|
-
"@livestore/common": "0.
|
15
|
-
"@livestore/utils": "0.
|
14
|
+
"@livestore/common": "0.4.0-dev.1",
|
15
|
+
"@livestore/utils": "0.4.0-dev.1"
|
16
16
|
},
|
17
17
|
"devDependencies": {
|
18
|
-
"@opentelemetry/sdk-trace-base": "^2.0.
|
19
|
-
"jsdom": "^26.
|
20
|
-
"typescript": "^5.
|
21
|
-
"vite": "^7.0.
|
18
|
+
"@opentelemetry/sdk-trace-base": "^2.0.1",
|
19
|
+
"jsdom": "^26.1.0",
|
20
|
+
"typescript": "^5.9.2",
|
21
|
+
"vite": "^7.0.6",
|
22
22
|
"vitest": "3.2.4",
|
23
|
-
"@livestore/utils-dev": "0.
|
24
|
-
"@livestore/adapter-web": "0.
|
23
|
+
"@livestore/utils-dev": "0.4.0-dev.1",
|
24
|
+
"@livestore/adapter-web": "0.4.0-dev.1"
|
25
25
|
},
|
26
26
|
"files": [
|
27
27
|
"package.json",
|
package/src/SqliteDbWrapper.ts
CHANGED
@@ -73,7 +73,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
73
73
|
prepare(queryStr: string): PreparedStatement {
|
74
74
|
return this.db.prepare(queryStr)
|
75
75
|
}
|
76
|
-
import(data: Uint8Array<
|
76
|
+
import(data: Uint8Array<ArrayBuffer> | SqliteDb<any, any>) {
|
77
77
|
return this.db.import(data)
|
78
78
|
}
|
79
79
|
close(): void {
|
@@ -85,7 +85,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
85
85
|
session(): SqliteDbSession {
|
86
86
|
return this.db.session()
|
87
87
|
}
|
88
|
-
makeChangeset(data: Uint8Array): SqliteDbChangeset {
|
88
|
+
makeChangeset(data: Uint8Array<ArrayBuffer>): SqliteDbChangeset {
|
89
89
|
return this.db.makeChangeset(data)
|
90
90
|
}
|
91
91
|
|
@@ -112,7 +112,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
112
112
|
|
113
113
|
withChangeset<TRes>(callback: () => TRes): {
|
114
114
|
result: TRes
|
115
|
-
changeset: { _tag: 'sessionChangeset'; data: Uint8Array
|
115
|
+
changeset: { _tag: 'sessionChangeset'; data: Uint8Array<ArrayBuffer>; debug: any } | { _tag: 'no-op' }
|
116
116
|
} {
|
117
117
|
const session = this.db.session()
|
118
118
|
const result = callback()
|
@@ -126,7 +126,7 @@ export class SqliteDbWrapper implements SqliteDb {
|
|
126
126
|
}
|
127
127
|
}
|
128
128
|
|
129
|
-
rollback(changeset: Uint8Array) {
|
129
|
+
rollback(changeset: Uint8Array<ArrayBuffer>) {
|
130
130
|
const invertedChangeset = this.db.makeChangeset(changeset).invert()
|
131
131
|
invertedChangeset.apply()
|
132
132
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { isNotNil } from '@livestore/utils'
|
2
|
-
import { Predicate } from '@livestore/utils/effect'
|
2
|
+
import { Equal, Hash, Predicate } from '@livestore/utils/effect'
|
3
3
|
import type * as otel from '@opentelemetry/api'
|
4
4
|
|
5
5
|
import * as RG from '../reactive.ts'
|
@@ -41,6 +41,8 @@ export interface SignalDef<T> extends LiveQueryDef<T, 'signal-def'> {
|
|
41
41
|
hash: string
|
42
42
|
label: string
|
43
43
|
make: (ctx: ReactivityGraphContext) => RcRef<ISignal<T>>
|
44
|
+
[Equal.symbol](that: SignalDef<T>): boolean
|
45
|
+
[Hash.symbol](): number
|
44
46
|
}
|
45
47
|
|
46
48
|
export interface ISignal<T> extends LiveQuery<T> {
|
@@ -77,6 +79,8 @@ export interface LiveQueryDef<TResult, TTag extends string = 'def'> {
|
|
77
79
|
make: (ctx: ReactivityGraphContext, otelContext?: otel.Context) => RcRef<LiveQuery<TResult> | ISignal<TResult>>
|
78
80
|
label: string
|
79
81
|
hash: string
|
82
|
+
[Equal.symbol](that: LiveQueryDef<TResult, TTag>): boolean
|
83
|
+
[Hash.symbol](): number
|
80
84
|
}
|
81
85
|
|
82
86
|
export namespace LiveQueryDef {
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { getDurationMsFromSpan } from '@livestore/common'
|
2
|
+
import { Equal, Hash } from '@livestore/utils/effect'
|
2
3
|
import * as otel from '@opentelemetry/api'
|
3
4
|
|
4
5
|
import type { Thunk } from '../reactive.ts'
|
@@ -35,6 +36,12 @@ export const computed = <TResult>(
|
|
35
36
|
// TODO we should figure out whether this could cause some problems and/or if there's a better way to do this
|
36
37
|
// NOTE `fn.toString()` doesn't work in Expo as it always produces `[native code]`
|
37
38
|
hash,
|
39
|
+
[Equal.symbol](that: LiveQueryDef<any>): boolean {
|
40
|
+
return this.hash === that.hash
|
41
|
+
},
|
42
|
+
[Hash.symbol](): number {
|
43
|
+
return Hash.string(this.hash)
|
44
|
+
},
|
38
45
|
}
|
39
46
|
|
40
47
|
return def
|
@@ -10,7 +10,7 @@ import {
|
|
10
10
|
UnexpectedError,
|
11
11
|
} from '@livestore/common'
|
12
12
|
import { deepEqual, shouldNeverHappen } from '@livestore/utils'
|
13
|
-
import { Predicate, Schema, TreeFormatter } from '@livestore/utils/effect'
|
13
|
+
import { Equal, Hash, Predicate, Schema, TreeFormatter } from '@livestore/utils/effect'
|
14
14
|
import * as otel from '@opentelemetry/api'
|
15
15
|
|
16
16
|
import type { Thunk } from '../reactive.ts'
|
@@ -131,6 +131,12 @@ export const queryDb: {
|
|
131
131
|
}),
|
132
132
|
label,
|
133
133
|
hash,
|
134
|
+
[Equal.symbol](that: LiveQueryDef<any>): boolean {
|
135
|
+
return this.hash === that.hash
|
136
|
+
},
|
137
|
+
[Hash.symbol](): number {
|
138
|
+
return Hash.string(this.hash)
|
139
|
+
},
|
134
140
|
}
|
135
141
|
|
136
142
|
return def
|
@@ -314,8 +320,11 @@ export class LiveStoreDbQuery<TResultSchema, TResult = TResultSchema> extends Li
|
|
314
320
|
|
315
321
|
const queriedTablesRef: { current: Set<string> | undefined } = { current: undefined }
|
316
322
|
|
317
|
-
const makeResultsEqual = (resultSchema: Schema.Schema<any, any>) =>
|
318
|
-
|
323
|
+
const makeResultsEqual = (resultSchema: Schema.Schema<any, any>) => {
|
324
|
+
// Creating the equivalence function eagerly in outer scope as it might be expensive
|
325
|
+
const eq = Schema.equivalence(resultSchema)
|
326
|
+
return (a: TResult, b: TResult) => (a === NOT_REFRESHED_YET || b === NOT_REFRESHED_YET ? false : eq(a, b))
|
327
|
+
}
|
319
328
|
|
320
329
|
// NOTE we try to create the equality function eagerly as it might be expensive
|
321
330
|
// TODO also support derived equality for `map` (probably will depend on having an easy way to transform a schema without an `encode` step)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { Equal, Hash } from '@livestore/utils/effect'
|
1
2
|
import { nanoid } from '@livestore/utils/nanoid'
|
2
3
|
|
3
4
|
import type * as RG from '../reactive.ts'
|
@@ -27,6 +28,12 @@ export const signal = <T>(
|
|
27
28
|
def,
|
28
29
|
}),
|
29
30
|
),
|
31
|
+
[Equal.symbol](that: SignalDef<T>): boolean {
|
32
|
+
return this.hash === that.hash
|
33
|
+
},
|
34
|
+
[Hash.symbol](): number {
|
35
|
+
return Hash.string(this.hash)
|
36
|
+
},
|
30
37
|
}
|
31
38
|
|
32
39
|
return def
|
package/src/mod.ts
CHANGED
@@ -35,11 +35,12 @@ export {
|
|
35
35
|
export { emptyDebugInfo, SqliteDbWrapper } from './SqliteDbWrapper.ts'
|
36
36
|
export { type CreateStoreOptions, createStore, createStorePromise } from './store/create-store.ts'
|
37
37
|
export { Store } from './store/store.ts'
|
38
|
-
export type { OtelOptions, QueryDebugInfo, RefreshReason } from './store/store-types.ts'
|
38
|
+
export type { OtelOptions, QueryDebugInfo, RefreshReason, Unsubscribe } from './store/store-types.ts'
|
39
39
|
export {
|
40
40
|
type LiveStoreContext,
|
41
41
|
type LiveStoreContextRunning,
|
42
42
|
makeShutdownDeferred,
|
43
43
|
type ShutdownDeferred,
|
44
44
|
} from './store/store-types.ts'
|
45
|
+
export { exposeDebugUtils } from './utils/dev.ts'
|
45
46
|
export * from './utils/stack-info.ts'
|
package/src/reactive.ts
CHANGED
@@ -211,8 +211,10 @@ export class ReactiveGraph<
|
|
211
211
|
|
212
212
|
private refreshCallbacks: Set<() => void> = new Set()
|
213
213
|
|
214
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for debugging
|
214
215
|
private nodeIdCounter = 0
|
215
216
|
private uniqueNodeId = () => `node-${++this.nodeIdCounter}`
|
217
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: for debugging
|
216
218
|
private refreshInfoIdCounter = 0
|
217
219
|
private uniqueRefreshInfoId = () => `refresh-info-${++this.refreshInfoIdCounter}`
|
218
220
|
|
@@ -1,16 +1,17 @@
|
|
1
|
-
import
|
2
|
-
Adapter,
|
3
|
-
BootStatus,
|
4
|
-
ClientSession,
|
5
|
-
ClientSessionDevtoolsChannel,
|
6
|
-
ClientSessionSyncProcessorSimulationParams,
|
7
|
-
IntentionalShutdownCause,
|
8
|
-
MigrationsReport,
|
1
|
+
import {
|
2
|
+
type Adapter,
|
3
|
+
type BootStatus,
|
4
|
+
type ClientSession,
|
5
|
+
type ClientSessionDevtoolsChannel,
|
6
|
+
type ClientSessionSyncProcessorSimulationParams,
|
7
|
+
type IntentionalShutdownCause,
|
8
|
+
type MigrationsReport,
|
9
|
+
provideOtel,
|
10
|
+
type SyncError,
|
11
|
+
UnexpectedError,
|
9
12
|
} from '@livestore/common'
|
10
|
-
import { provideOtel, UnexpectedError } from '@livestore/common'
|
11
13
|
import type { LiveStoreSchema } from '@livestore/common/schema'
|
12
14
|
import { isDevEnv, LS_DEV } from '@livestore/utils'
|
13
|
-
import type { Cause, Schema } from '@livestore/utils/effect'
|
14
15
|
import {
|
15
16
|
Context,
|
16
17
|
Deferred,
|
@@ -24,6 +25,7 @@ import {
|
|
24
25
|
OtelTracer,
|
25
26
|
Queue,
|
26
27
|
Runtime,
|
28
|
+
type Schema,
|
27
29
|
Scope,
|
28
30
|
TaskTracing,
|
29
31
|
} from '@livestore/utils/effect'
|
@@ -96,7 +98,7 @@ export interface CreateStoreOptions<TSchema extends LiveStoreSchema, TContext =
|
|
96
98
|
migrationsReport: MigrationsReport
|
97
99
|
parentSpan: otel.Span
|
98
100
|
},
|
99
|
-
) =>
|
101
|
+
) => Effect.SyncOrPromiseOrEffect<void, unknown, OtelTracer.OtelTracer | LiveStoreContextRunning>
|
100
102
|
batchUpdates?: (run: () => void) => void
|
101
103
|
/**
|
102
104
|
* Whether to disable devtools.
|
@@ -131,7 +133,7 @@ export interface CreateStoreOptions<TSchema extends LiveStoreSchema, TContext =
|
|
131
133
|
}
|
132
134
|
|
133
135
|
/** Create a new LiveStore Store */
|
134
|
-
export const createStorePromise = async <TSchema extends LiveStoreSchema = LiveStoreSchema, TContext = {}>({
|
136
|
+
export const createStorePromise = async <TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>({
|
135
137
|
signal,
|
136
138
|
otelOptions,
|
137
139
|
...options
|
@@ -162,7 +164,7 @@ export const createStorePromise = async <TSchema extends LiveStoreSchema = LiveS
|
|
162
164
|
Effect.runPromise,
|
163
165
|
)
|
164
166
|
|
165
|
-
export const createStore = <TSchema extends LiveStoreSchema = LiveStoreSchema, TContext = {}>({
|
167
|
+
export const createStore = <TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>({
|
166
168
|
schema,
|
167
169
|
adapter,
|
168
170
|
storeId,
|
@@ -215,9 +217,9 @@ export const createStore = <TSchema extends LiveStoreSchema = LiveStoreSchema, T
|
|
215
217
|
|
216
218
|
const runtime = yield* Effect.runtime<Scope.Scope>()
|
217
219
|
|
218
|
-
const shutdown = (
|
220
|
+
const shutdown = (exit: Exit.Exit<IntentionalShutdownCause, UnexpectedError | SyncError>) =>
|
219
221
|
Effect.gen(function* () {
|
220
|
-
yield* Scope.close(lifetimeScope,
|
222
|
+
yield* Scope.close(lifetimeScope, exit).pipe(
|
221
223
|
Effect.logWarnIfTakesLongerThan({ label: '@livestore/livestore:shutdown', duration: 500 }),
|
222
224
|
Effect.timeout(1000),
|
223
225
|
Effect.catchTag('TimeoutException', () =>
|
@@ -226,7 +228,7 @@ export const createStore = <TSchema extends LiveStoreSchema = LiveStoreSchema, T
|
|
226
228
|
)
|
227
229
|
|
228
230
|
if (shutdownDeferred) {
|
229
|
-
yield* Deferred.
|
231
|
+
yield* Deferred.done(shutdownDeferred, exit)
|
230
232
|
}
|
231
233
|
|
232
234
|
yield* Effect.logDebug('LiveStore shutdown complete')
|
package/src/store/store-types.ts
CHANGED
@@ -3,6 +3,7 @@ import type {
|
|
3
3
|
ClientSessionSyncProcessorSimulationParams,
|
4
4
|
IntentionalShutdownCause,
|
5
5
|
StoreInterrupted,
|
6
|
+
SyncError,
|
6
7
|
UnexpectedError,
|
7
8
|
} from '@livestore/common'
|
8
9
|
import type { EventSequenceNumber, LiveStoreEvent, LiveStoreSchema } from '@livestore/common/schema'
|
@@ -22,13 +23,16 @@ export type LiveStoreContext =
|
|
22
23
|
}
|
23
24
|
| {
|
24
25
|
stage: 'shutdown'
|
25
|
-
cause: IntentionalShutdownCause | StoreInterrupted
|
26
|
+
cause: IntentionalShutdownCause | StoreInterrupted | SyncError
|
26
27
|
}
|
27
28
|
|
28
|
-
export type ShutdownDeferred = Deferred.Deferred<
|
29
|
+
export type ShutdownDeferred = Deferred.Deferred<
|
30
|
+
IntentionalShutdownCause,
|
31
|
+
UnexpectedError | SyncError | StoreInterrupted
|
32
|
+
>
|
29
33
|
export const makeShutdownDeferred: Effect.Effect<ShutdownDeferred> = Deferred.make<
|
30
|
-
|
31
|
-
UnexpectedError |
|
34
|
+
IntentionalShutdownCause,
|
35
|
+
UnexpectedError | SyncError | StoreInterrupted
|
32
36
|
>()
|
33
37
|
|
34
38
|
export type LiveStoreContextRunning = {
|
@@ -41,7 +45,7 @@ export type OtelOptions = {
|
|
41
45
|
rootSpanContext: otel.Context
|
42
46
|
}
|
43
47
|
|
44
|
-
export type StoreOptions<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext = {}> = {
|
48
|
+
export type StoreOptions<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}> = {
|
45
49
|
clientSession: ClientSession
|
46
50
|
schema: TSchema
|
47
51
|
storeId: string
|
package/src/store/store.ts
CHANGED
@@ -22,7 +22,18 @@ import type { LiveStoreSchema } from '@livestore/common/schema'
|
|
22
22
|
import { getEventDef, LiveStoreEvent, SystemTables } from '@livestore/common/schema'
|
23
23
|
import { assertNever, isDevEnv, notYetImplemented } from '@livestore/utils'
|
24
24
|
import type { Scope } from '@livestore/utils/effect'
|
25
|
-
import {
|
25
|
+
import {
|
26
|
+
Cause,
|
27
|
+
Effect,
|
28
|
+
Exit,
|
29
|
+
Fiber,
|
30
|
+
Inspectable,
|
31
|
+
Option,
|
32
|
+
OtelTracer,
|
33
|
+
Runtime,
|
34
|
+
Schema,
|
35
|
+
Stream,
|
36
|
+
} from '@livestore/utils/effect'
|
26
37
|
import { nanoid } from '@livestore/utils/nanoid'
|
27
38
|
import * as otel from '@opentelemetry/api'
|
28
39
|
|
@@ -54,7 +65,7 @@ if (isDevEnv()) {
|
|
54
65
|
exposeDebugUtils()
|
55
66
|
}
|
56
67
|
|
57
|
-
export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext = {}> extends Inspectable.Class {
|
68
|
+
export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}> extends Inspectable.Class {
|
58
69
|
readonly storeId: string
|
59
70
|
reactivityGraph: ReactivityGraph
|
60
71
|
sqliteDbWrapper: SqliteDbWrapper
|
@@ -68,6 +79,9 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
68
79
|
*/
|
69
80
|
tableRefs: { [key: string]: Ref<null, ReactivityGraphContext, RefreshReason> }
|
70
81
|
|
82
|
+
/** Tracks whether the store has been shut down */
|
83
|
+
private isShutdown = false
|
84
|
+
|
71
85
|
private effectContext: {
|
72
86
|
runtime: Runtime.Runtime<Scope.Scope>
|
73
87
|
lifetimeScope: Scope.Scope
|
@@ -168,7 +182,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
168
182
|
}
|
169
183
|
|
170
184
|
let sessionChangeset:
|
171
|
-
| { _tag: 'sessionChangeset'; data: Uint8Array
|
185
|
+
| { _tag: 'sessionChangeset'; data: Uint8Array<ArrayBuffer>; debug: any }
|
172
186
|
| { _tag: 'no-op' }
|
173
187
|
| { _tag: 'unset' } = { _tag: 'unset' }
|
174
188
|
|
@@ -282,6 +296,15 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
282
296
|
return this.clientSession.clientId
|
283
297
|
}
|
284
298
|
|
299
|
+
private checkShutdown = (operation: string): void => {
|
300
|
+
if (this.isShutdown) {
|
301
|
+
throw new UnexpectedError({
|
302
|
+
cause: `Store has been shut down (while performing "${operation}").`,
|
303
|
+
note: `You cannot perform this operation after the store has been shut down.`,
|
304
|
+
})
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
285
308
|
/**
|
286
309
|
* Subscribe to the results of a query
|
287
310
|
* Returns a function to cancel the subscription.
|
@@ -309,8 +332,10 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
309
332
|
/** If provided, the stack info will be added to the `activeSubscriptions` set of the query */
|
310
333
|
stackInfo?: StackInfo
|
311
334
|
},
|
312
|
-
): Unsubscribe =>
|
313
|
-
this.
|
335
|
+
): Unsubscribe => {
|
336
|
+
this.checkShutdown('subscribe')
|
337
|
+
|
338
|
+
return this.otel.tracer.startActiveSpan(
|
314
339
|
`LiveStore.subscribe`,
|
315
340
|
{ attributes: { label: options?.label, queryLabel: isQueryBuilder(query) ? query.toString() : query.label } },
|
316
341
|
options?.otelContext ?? this.otel.queriesSpanContext,
|
@@ -369,6 +394,7 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
369
394
|
return unsubscribe
|
370
395
|
},
|
371
396
|
)
|
397
|
+
}
|
372
398
|
|
373
399
|
subscribeStream = <TResult>(
|
374
400
|
query$: LiveQueryDef<TResult>,
|
@@ -417,6 +443,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
417
443
|
| { query: string; bindValues: Bindable; schema?: Schema.Schema<TResult> },
|
418
444
|
options?: { otelContext?: otel.Context; debugRefreshReason?: RefreshReason },
|
419
445
|
): TResult => {
|
446
|
+
this.checkShutdown('query')
|
447
|
+
|
420
448
|
if (typeof query === 'object' && 'query' in query && 'bindValues' in query) {
|
421
449
|
const res = this.sqliteDbWrapper.cachedSelect(query.query, prepareBindValues(query.bindValues, query.query), {
|
422
450
|
otelContext: options?.otelContext,
|
@@ -438,6 +466,12 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
438
466
|
|
439
467
|
const sqlRes = query.asSql()
|
440
468
|
const schema = getResultSchema(query)
|
469
|
+
|
470
|
+
// Replace SessionIdSymbol in bind values before executing the query
|
471
|
+
if (sqlRes.bindValues) {
|
472
|
+
replaceSessionIdSymbol(sqlRes.bindValues, this.clientSession.sessionId)
|
473
|
+
}
|
474
|
+
|
441
475
|
const rawRes = this.sqliteDbWrapper.cachedSelect(sqlRes.query, sqlRes.bindValues as any as PreparedBindValues, {
|
442
476
|
otelContext: options?.otelContext,
|
443
477
|
queriedTables: new Set([query[QueryBuilderAstSymbol].tableDef.sqliteDef.name]),
|
@@ -473,6 +507,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
473
507
|
* ```
|
474
508
|
*/
|
475
509
|
setSignal = <T>(signalDef: SignalDef<T>, value: T | ((prev: T) => T)): void => {
|
510
|
+
this.checkShutdown('setSignal')
|
511
|
+
|
476
512
|
const signalRef = signalDef.make(this.reactivityGraph.context!)
|
477
513
|
const newValue: T = typeof value === 'function' ? (value as any)(signalRef.value.get()) : value
|
478
514
|
signalRef.value.set(newValue)
|
@@ -557,6 +593,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
557
593
|
) => void,
|
558
594
|
): void
|
559
595
|
} = (firstEventOrTxnFnOrOptions: any, ...restEvents: any[]) => {
|
596
|
+
this.checkShutdown('commit')
|
597
|
+
|
560
598
|
const { events, options } = this.getCommitArgs(firstEventOrTxnFnOrOptions, restEvents)
|
561
599
|
|
562
600
|
for (const event of events) {
|
@@ -660,10 +698,14 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
660
698
|
* ```
|
661
699
|
*/
|
662
700
|
events = (_options?: StoreEventsOptions<TSchema>): AsyncIterable<LiveStoreEvent.ForSchema<TSchema>> => {
|
701
|
+
this.checkShutdown('events')
|
702
|
+
|
663
703
|
return notYetImplemented(`store.events() is not yet implemented but planned soon`)
|
664
704
|
}
|
665
705
|
|
666
706
|
eventsStream = (_options?: StoreEventsOptions<TSchema>): Stream.Stream<LiveStoreEvent.ForSchema<TSchema>> => {
|
707
|
+
this.checkShutdown('eventsStream')
|
708
|
+
|
667
709
|
return notYetImplemented(`store.eventsStream() is not yet implemented but planned soon`)
|
668
710
|
}
|
669
711
|
|
@@ -672,6 +714,8 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
672
714
|
* We might need a better solution for this. Let's see.
|
673
715
|
*/
|
674
716
|
manualRefresh = (options?: { label?: string }) => {
|
717
|
+
this.checkShutdown('manualRefresh')
|
718
|
+
|
675
719
|
const { label } = options ?? {}
|
676
720
|
this.otel.tracer.startActiveSpan(
|
677
721
|
'LiveStore:manualRefresh',
|
@@ -690,10 +734,25 @@ export class Store<TSchema extends LiveStoreSchema = LiveStoreSchema, TContext =
|
|
690
734
|
*
|
691
735
|
* This is called automatically when the store was created using the React or Effect API.
|
692
736
|
*/
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
737
|
+
shutdownPromise = async (cause?: UnexpectedError) => {
|
738
|
+
this.checkShutdown('shutdownPromise')
|
739
|
+
|
740
|
+
this.isShutdown = true
|
741
|
+
await this.shutdown(cause ? Cause.fail(cause) : undefined).pipe(this.runEffectFork, Fiber.join, Effect.runPromise)
|
742
|
+
}
|
743
|
+
|
744
|
+
/**
|
745
|
+
* Shuts down the store and closes the client session.
|
746
|
+
*
|
747
|
+
* This is called automatically when the store was created using the React or Effect API.
|
748
|
+
*/
|
749
|
+
shutdown = (cause?: Cause.Cause<UnexpectedError>): Effect.Effect<void> => {
|
750
|
+
this.checkShutdown('shutdown')
|
751
|
+
|
752
|
+
this.isShutdown = true
|
753
|
+
return this.clientSession.shutdown(
|
754
|
+
cause ? Exit.failCause(cause) : Exit.succeed(IntentionalShutdownCause.make({ reason: 'manual' })),
|
755
|
+
)
|
697
756
|
}
|
698
757
|
|
699
758
|
/**
|