@spooky-sync/client-solid2 0.0.1-canary.200 → 0.0.1-canary.201
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/AGENTS.md +77 -0
- package/README.md +2 -2
- package/dist/index.cjs +340 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -9
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +114 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +339 -51
- package/dist/index.js.map +1 -1
- package/package.json +17 -16
- package/skills/sp00ky-solid2/SKILL.md +1 -1
- package/src/index.ts +36 -11
- package/src/lib/Blurhash.ts +64 -0
- package/src/lib/BucketImage.ts +170 -0
- package/src/lib/__tests__/rc-semantics.test.ts +9 -5
- package/src/lib/create-query.ts +135 -87
- package/src/lib/merge-rows.ts +46 -0
- package/src/lib/use-blurhash.ts +82 -0
- package/src/lib/use-bucket-image.ts +108 -0
- package/tsdown.config.ts +2 -0
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Sp00kyClient, fileToUint8Array, semverGt } from "@spooky-sync/core";
|
|
1
|
+
import { Sp00kyClient, decodeBlurhash, fileToUint8Array, semverGt } from "@spooky-sync/core";
|
|
2
2
|
import { RecordId, Uuid } from "surrealdb";
|
|
3
|
-
import { createComponent, createContext, createEffect, createMemo,
|
|
3
|
+
import { children, createComponent, createContext, createEffect, createMemo, createSignal, createStore, merge, onCleanup, onSettled, snapshot, useContext } from "solid-js";
|
|
4
4
|
|
|
5
5
|
//#region src/lib/conflate.ts
|
|
6
6
|
/**
|
|
@@ -124,6 +124,42 @@ function usePendingMutations() {
|
|
|
124
124
|
return fromSubscription((cb) => db.subscribeToPendingMutations(cb), db.pendingMutationCount);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/lib/merge-rows.ts
|
|
129
|
+
/**
|
|
130
|
+
* Keyed in-place merge of a fresh row list into a store draft array.
|
|
131
|
+
*
|
|
132
|
+
* Why not `reconcile(rows, 'id')`: in Solid 2 rc.1 a reconcile REPLACES the row
|
|
133
|
+
* objects, so a component that captured `data()[0]` sees a different object
|
|
134
|
+
* after the next emission, and anything keyed on row identity (`<For>` without
|
|
135
|
+
* an explicit key, a memo comparing rows) re-creates its subtree on every live
|
|
136
|
+
* update. Mutating the draft in place keeps the identity AND keeps updates
|
|
137
|
+
* fine-grained: only the fields that actually changed are written, so a row
|
|
138
|
+
* whose data is unchanged notifies nobody.
|
|
139
|
+
*
|
|
140
|
+
* Rows are matched by `key` (default `id`); unmatched incoming rows are
|
|
141
|
+
* inserted as-is and trailing rows are dropped, so add / remove / reorder all
|
|
142
|
+
* reach coarse readers.
|
|
143
|
+
*/
|
|
144
|
+
function mergeRows(draft, next, key = "id") {
|
|
145
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
146
|
+
for (const row of draft) {
|
|
147
|
+
const k = row?.[key];
|
|
148
|
+
if (k !== void 0) byKey.set(String(k), row);
|
|
149
|
+
}
|
|
150
|
+
for (let i = 0; i < next.length; i++) {
|
|
151
|
+
const incoming = next[i];
|
|
152
|
+
const k = incoming?.[key];
|
|
153
|
+
const reuse = k !== void 0 ? byKey.get(String(k)) : void 0;
|
|
154
|
+
if (reuse) {
|
|
155
|
+
for (const field of Object.keys(incoming)) if (reuse[field] !== incoming[field]) reuse[field] = incoming[field];
|
|
156
|
+
for (const field of Object.keys(snapshot(reuse))) if (!(field in incoming)) delete reuse[field];
|
|
157
|
+
if (draft[i] !== reuse) draft[i] = reuse;
|
|
158
|
+
} else if (draft[i] !== incoming) draft[i] = incoming;
|
|
159
|
+
}
|
|
160
|
+
if (draft.length > next.length) draft.splice(next.length);
|
|
161
|
+
}
|
|
162
|
+
|
|
127
163
|
//#endregion
|
|
128
164
|
//#region src/lib/create-query.ts
|
|
129
165
|
function createQuery(dbOrQuery, queryOrOptions, maybeOptions) {
|
|
@@ -144,54 +180,76 @@ function createQuery(dbOrQuery, queryOrOptions, maybeOptions) {
|
|
|
144
180
|
const [isFetched, setIsFetched] = createSignal(false, { ownedWrite: true });
|
|
145
181
|
const [isFetching, setIsFetching] = createSignal(false, { ownedWrite: true });
|
|
146
182
|
let activeHash;
|
|
147
|
-
const store =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
183
|
+
const [store, setStore] = createStore({ value: null });
|
|
184
|
+
let runId = 0;
|
|
185
|
+
let readyWaiters = [];
|
|
186
|
+
const wakeReady = () => {
|
|
187
|
+
const waiters = readyWaiters;
|
|
188
|
+
readyWaiters = [];
|
|
189
|
+
for (const w of waiters) w();
|
|
190
|
+
};
|
|
191
|
+
createEffect(() => {
|
|
192
|
+
return {
|
|
193
|
+
enabled: options?.enabled?.() ?? true,
|
|
194
|
+
query: typeof finalQuery === "function" ? finalQuery() : finalQuery
|
|
195
|
+
};
|
|
196
|
+
}, ({ enabled, query }) => {
|
|
197
|
+
const myRun = ++runId;
|
|
155
198
|
setIsFetched(false);
|
|
156
199
|
setError(void 0);
|
|
157
|
-
|
|
200
|
+
if (!enabled || !query) return;
|
|
158
201
|
const cleanups = [];
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
202
|
+
let disposed = false;
|
|
203
|
+
const addCleanup = (c) => {
|
|
204
|
+
if (!c) return;
|
|
205
|
+
if (typeof c === "function") {
|
|
206
|
+
if (disposed) c();
|
|
207
|
+
else cleanups.push(c);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
Promise.resolve(c).then((fn) => {
|
|
211
|
+
if (typeof fn !== "function") return;
|
|
212
|
+
if (disposed) fn();
|
|
213
|
+
else cleanups.push(fn);
|
|
214
|
+
});
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Registration can fail — the canonical case is the SSP answering 503
|
|
218
|
+
* NOT_READY while it bootstraps. Surface it as `error()` instead of
|
|
219
|
+
* throwing into the graph: the sync scheduler retries the registration
|
|
220
|
+
* underneath, so a transient failure still recovers, and a spinner
|
|
221
|
+
* driven by `isLoading()` resolves via `error()`.
|
|
222
|
+
*/
|
|
223
|
+
query.run().then(({ hash }) => {
|
|
224
|
+
if (disposed || myRun !== runId) return;
|
|
172
225
|
activeHash = hash;
|
|
173
|
-
|
|
174
|
-
const it = conflate((cb) => sp00ky.subscribe(hash, cb, { immediate: true }))[Symbol.asyncIterator]();
|
|
175
|
-
iterators.push(it);
|
|
226
|
+
addCleanup(sp00ky.subscribeQueryStatus(hash, (status) => setIsFetching(status === "fetching"), { immediate: true }));
|
|
176
227
|
let isFirstCall = true;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
228
|
+
addCleanup(sp00ky.subscribe(hash, (rows) => {
|
|
229
|
+
if (disposed || myRun !== runId) return;
|
|
230
|
+
const queryData = query.isOne ? rows[0] ?? null : rows;
|
|
231
|
+
const hasData = query.isOne ? queryData !== null && queryData !== void 0 : rows.length > 0;
|
|
232
|
+
if (!isFirstCall || hasData) {
|
|
233
|
+
setIsFetched(true);
|
|
234
|
+
wakeReady();
|
|
235
|
+
}
|
|
184
236
|
isFirstCall = false;
|
|
185
237
|
const t0 = performance.now();
|
|
186
|
-
|
|
238
|
+
setStore((s) => {
|
|
239
|
+
if (query.isOne || queryData === null || !Array.isArray(s.value)) s.value = queryData;
|
|
240
|
+
else mergeRows(s.value, queryData);
|
|
241
|
+
});
|
|
187
242
|
sp00ky.reportFrontendTiming(hash, performance.now() - t0);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
243
|
+
}, { immediate: true }));
|
|
244
|
+
}).catch((err) => {
|
|
245
|
+
if (disposed || myRun !== runId) return;
|
|
190
246
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
247
|
+
wakeReady();
|
|
248
|
+
});
|
|
249
|
+
return () => {
|
|
250
|
+
disposed = true;
|
|
251
|
+
for (const c of cleanups) c();
|
|
252
|
+
};
|
|
195
253
|
});
|
|
196
254
|
const emptyList = [];
|
|
197
255
|
const data = () => {
|
|
@@ -204,9 +262,9 @@ function createQuery(dbOrQuery, queryOrOptions, maybeOptions) {
|
|
|
204
262
|
};
|
|
205
263
|
const readyGate = createMemo(async () => {
|
|
206
264
|
if (isFetched() || error()) return true;
|
|
207
|
-
await new Promise(() =>
|
|
265
|
+
await new Promise((resolve) => readyWaiters.push(resolve));
|
|
208
266
|
return true;
|
|
209
|
-
});
|
|
267
|
+
}, { lazy: true });
|
|
210
268
|
const ready = () => {
|
|
211
269
|
readyGate();
|
|
212
270
|
return data();
|
|
@@ -618,6 +676,222 @@ function useDownloadFile(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeO
|
|
|
618
676
|
};
|
|
619
677
|
}
|
|
620
678
|
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region src/lib/use-blurhash.ts
|
|
681
|
+
function useBlurhash(dbOrBucketName, bucketNameOrPath, maybePath) {
|
|
682
|
+
let db;
|
|
683
|
+
let bucketName;
|
|
684
|
+
let path;
|
|
685
|
+
if (typeof dbOrBucketName === "string") {
|
|
686
|
+
db = useDb();
|
|
687
|
+
bucketName = dbOrBucketName;
|
|
688
|
+
path = bucketNameOrPath;
|
|
689
|
+
} else {
|
|
690
|
+
db = dbOrBucketName;
|
|
691
|
+
bucketName = bucketNameOrPath;
|
|
692
|
+
path = maybePath;
|
|
693
|
+
}
|
|
694
|
+
const [hash, setHash] = createSignal(null, { ownedWrite: true });
|
|
695
|
+
const [isLoading, setIsLoading] = createSignal(false, { ownedWrite: true });
|
|
696
|
+
createEffect(() => path(), (filePath) => {
|
|
697
|
+
if (!filePath) {
|
|
698
|
+
setHash(null);
|
|
699
|
+
setIsLoading(false);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
let cancelled = false;
|
|
703
|
+
setIsLoading(true);
|
|
704
|
+
db.bucket(bucketName).blurhash(filePath).then((result) => {
|
|
705
|
+
if (cancelled) return;
|
|
706
|
+
setHash(result);
|
|
707
|
+
setIsLoading(false);
|
|
708
|
+
}).catch(() => {
|
|
709
|
+
if (cancelled) return;
|
|
710
|
+
setHash(null);
|
|
711
|
+
setIsLoading(false);
|
|
712
|
+
});
|
|
713
|
+
return () => {
|
|
714
|
+
cancelled = true;
|
|
715
|
+
};
|
|
716
|
+
});
|
|
717
|
+
return {
|
|
718
|
+
hash,
|
|
719
|
+
isLoading
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region src/lib/use-bucket-image.ts
|
|
725
|
+
function useBucketImage(dbOrBucketName, bucketNameOrPath, pathOrOptions, maybeOptions) {
|
|
726
|
+
let db;
|
|
727
|
+
let bucketName;
|
|
728
|
+
let path;
|
|
729
|
+
let options;
|
|
730
|
+
if (typeof dbOrBucketName === "string") {
|
|
731
|
+
db = useDb();
|
|
732
|
+
bucketName = dbOrBucketName;
|
|
733
|
+
path = bucketNameOrPath;
|
|
734
|
+
options = pathOrOptions ?? {};
|
|
735
|
+
} else {
|
|
736
|
+
db = dbOrBucketName;
|
|
737
|
+
bucketName = bucketNameOrPath;
|
|
738
|
+
path = pathOrOptions;
|
|
739
|
+
options = maybeOptions ?? {};
|
|
740
|
+
}
|
|
741
|
+
const wantHash = options.blurhash !== false;
|
|
742
|
+
const { hash } = useBlurhash(db, bucketName, () => wantHash ? path() : null);
|
|
743
|
+
const file = useDownloadFile(db, bucketName, path, options);
|
|
744
|
+
const [ready, setReady] = createSignal(false, { ownedWrite: true });
|
|
745
|
+
createEffect(file.url, () => {
|
|
746
|
+
setReady(false);
|
|
747
|
+
}, { defer: true });
|
|
748
|
+
const gate = (img) => {
|
|
749
|
+
const done = () => setReady(true);
|
|
750
|
+
if (typeof img.decode === "function") img.decode().then(done, done);
|
|
751
|
+
else if (img.complete) done();
|
|
752
|
+
else {
|
|
753
|
+
img.onload = done;
|
|
754
|
+
img.onerror = done;
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
return {
|
|
758
|
+
...file,
|
|
759
|
+
blurhash: hash,
|
|
760
|
+
ready,
|
|
761
|
+
gate
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
//#endregion
|
|
766
|
+
//#region src/lib/Blurhash.ts
|
|
767
|
+
/**
|
|
768
|
+
* A blurhash painted onto a canvas, once per hash change. Size the canvas via
|
|
769
|
+
* `class`/`style` (e.g. `absolute inset-0 w-full h-full`); the internal decode
|
|
770
|
+
* resolution stays tiny regardless of the displayed size.
|
|
771
|
+
*/
|
|
772
|
+
function Blurhash(props) {
|
|
773
|
+
if (typeof document === "undefined") return null;
|
|
774
|
+
const canvas = document.createElement("canvas");
|
|
775
|
+
createEffect(() => props.class ?? "", (className) => {
|
|
776
|
+
canvas.className = className;
|
|
777
|
+
});
|
|
778
|
+
createEffect(() => props.style ?? "", (style) => {
|
|
779
|
+
canvas.style.cssText = style;
|
|
780
|
+
});
|
|
781
|
+
createEffect(() => ({
|
|
782
|
+
width: props.width ?? 32,
|
|
783
|
+
height: props.height ?? 32,
|
|
784
|
+
hash: props.hash,
|
|
785
|
+
punch: props.punch ?? 1
|
|
786
|
+
}), ({ width, height, hash, punch }) => {
|
|
787
|
+
canvas.width = width;
|
|
788
|
+
canvas.height = height;
|
|
789
|
+
if (!hash) return;
|
|
790
|
+
try {
|
|
791
|
+
const pixels = decodeBlurhash(hash, width, height, punch);
|
|
792
|
+
const ctx = canvas.getContext("2d");
|
|
793
|
+
if (!ctx) return;
|
|
794
|
+
const imageData = ctx.createImageData(width, height);
|
|
795
|
+
imageData.data.set(pixels);
|
|
796
|
+
ctx.putImageData(imageData, 0, 0);
|
|
797
|
+
} catch {}
|
|
798
|
+
});
|
|
799
|
+
return canvas;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
//#endregion
|
|
803
|
+
//#region src/lib/BucketImage.ts
|
|
804
|
+
const LAYER_STYLE = "position:absolute;inset:0;width:100%;height:100%;";
|
|
805
|
+
/**
|
|
806
|
+
* A bucket image that never pops in: it layers (bottom to top) your `fallback`
|
|
807
|
+
* plate, the automatically stored blurhash, and the real image, which stays
|
|
808
|
+
* transparent until the bitmap is DECODED and then crossfades over the
|
|
809
|
+
* placeholders. Placeholder layers unmount once the fade settles. Respects
|
|
810
|
+
* prefers-reduced-motion (instant swap). The container is made
|
|
811
|
+
* `position: relative` unless your `class` positions it already.
|
|
812
|
+
*
|
|
813
|
+
* ```tsx
|
|
814
|
+
* <BucketImage bucket="covers" path={row.cover_key} class="absolute inset-0"
|
|
815
|
+
* fallback={<MyPlate />} alt="" />
|
|
816
|
+
* ```
|
|
817
|
+
*/
|
|
818
|
+
function BucketImage(props) {
|
|
819
|
+
if (typeof document === "undefined") return null;
|
|
820
|
+
const image = useBucketImage(props.bucket, () => props.path, {
|
|
821
|
+
...props.options,
|
|
822
|
+
blurhash: props.blurhash !== false
|
|
823
|
+
});
|
|
824
|
+
const reducedMotion = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
825
|
+
const root = document.createElement("div");
|
|
826
|
+
createEffect(() => props.class ?? "", (className) => {
|
|
827
|
+
root.className = className;
|
|
828
|
+
});
|
|
829
|
+
onSettled(() => {
|
|
830
|
+
if (getComputedStyle(root).position === "static") root.style.position = "relative";
|
|
831
|
+
});
|
|
832
|
+
const placeholder = document.createElement("div");
|
|
833
|
+
placeholder.style.cssText = LAYER_STYLE;
|
|
834
|
+
const fallbackHolder = document.createElement("div");
|
|
835
|
+
fallbackHolder.style.cssText = LAYER_STYLE;
|
|
836
|
+
placeholder.append(fallbackHolder);
|
|
837
|
+
const resolvedFallback = children(() => props.fallback);
|
|
838
|
+
createEffect(() => resolvedFallback.toArray().filter((node) => node instanceof Node), (nodes) => {
|
|
839
|
+
fallbackHolder.replaceChildren(...nodes);
|
|
840
|
+
});
|
|
841
|
+
const hashCanvas = Blurhash({
|
|
842
|
+
get hash() {
|
|
843
|
+
return image.blurhash();
|
|
844
|
+
},
|
|
845
|
+
style: LAYER_STYLE
|
|
846
|
+
});
|
|
847
|
+
if (hashCanvas instanceof Node) placeholder.append(hashCanvas);
|
|
848
|
+
const img = document.createElement("img");
|
|
849
|
+
img.decoding = "async";
|
|
850
|
+
img.style.cssText = `${LAYER_STYLE}opacity:0;`;
|
|
851
|
+
createEffect(() => props.imgClass ?? "", (className) => {
|
|
852
|
+
img.className = className;
|
|
853
|
+
});
|
|
854
|
+
createEffect(() => props.fit ?? "cover", (fit) => {
|
|
855
|
+
img.style.objectFit = fit;
|
|
856
|
+
});
|
|
857
|
+
createEffect(() => props.alt ?? "", (alt) => {
|
|
858
|
+
img.alt = alt;
|
|
859
|
+
});
|
|
860
|
+
createEffect(() => reducedMotion ? "none" : `opacity ${props.transition ?? 300}ms ${props.easing ?? "cubic-bezier(0.16, 1, 0.3, 1)"}`, (transition) => {
|
|
861
|
+
img.style.transition = transition;
|
|
862
|
+
});
|
|
863
|
+
createEffect(() => image.url(), (url) => {
|
|
864
|
+
if (!url) {
|
|
865
|
+
img.removeAttribute("src");
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
img.src = url;
|
|
869
|
+
image.gate(img);
|
|
870
|
+
});
|
|
871
|
+
createEffect(() => image.ready(), (ready) => {
|
|
872
|
+
img.style.opacity = ready ? "1" : "0";
|
|
873
|
+
});
|
|
874
|
+
const [settled, setSettled] = createSignal(false, { ownedWrite: true });
|
|
875
|
+
createEffect(() => ({
|
|
876
|
+
ready: image.ready(),
|
|
877
|
+
transition: props.transition ?? 300
|
|
878
|
+
}), ({ ready, transition }) => {
|
|
879
|
+
if (!ready) {
|
|
880
|
+
setSettled(false);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const wait = (reducedMotion ? 0 : transition) + 120;
|
|
884
|
+
const timer = setTimeout(() => setSettled(true), wait);
|
|
885
|
+
return () => clearTimeout(timer);
|
|
886
|
+
});
|
|
887
|
+
createEffect(() => settled(), (isSettled) => {
|
|
888
|
+
if (isSettled) placeholder.remove();
|
|
889
|
+
else if (!placeholder.isConnected) root.insertBefore(placeholder, img);
|
|
890
|
+
});
|
|
891
|
+
root.append(placeholder, img);
|
|
892
|
+
return root;
|
|
893
|
+
}
|
|
894
|
+
|
|
621
895
|
//#endregion
|
|
622
896
|
//#region src/lib/Sp00kyProvider.ts
|
|
623
897
|
function Sp00kyProvider(props) {
|
|
@@ -712,7 +986,20 @@ function createSubmission(fn) {
|
|
|
712
986
|
* NOTE: keep in sync with packages/client-solid/src/index.ts (SyncedDb).
|
|
713
987
|
* Copied rather than shared so this package's dependency graph never pulls
|
|
714
988
|
* in solid-js 1.x; fold the two together once client-solid moves to Solid 2.
|
|
989
|
+
* Solid-2-only differences: `delete` accepts 'bound RecordId', and write
|
|
990
|
+
* payloads go through `snapshot()` (see `unproxy` below).
|
|
715
991
|
*/
|
|
992
|
+
/**
|
|
993
|
+
* Solid 2 stores wrap every object read out of them (query rows, their nested
|
|
994
|
+
* arrays, `createStore` docs) in a Proxy. Those proxies cannot cross
|
|
995
|
+
* `postMessage` (the sqlite and shared-tabs workers): structuredClone throws
|
|
996
|
+
* DataCloneError. `snapshot()` returns the underlying plain value for store
|
|
997
|
+
* proxies and passes anything else through untouched.
|
|
998
|
+
*/
|
|
999
|
+
function unproxy(value) {
|
|
1000
|
+
if (value === null || typeof value !== "object") return value;
|
|
1001
|
+
return snapshot(value);
|
|
1002
|
+
}
|
|
716
1003
|
var SyncedDb = class {
|
|
717
1004
|
constructor(config) {
|
|
718
1005
|
this.sp00ky = null;
|
|
@@ -749,25 +1036,26 @@ var SyncedDb = class {
|
|
|
749
1036
|
*/
|
|
750
1037
|
async create(id, payload) {
|
|
751
1038
|
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
752
|
-
await this.sp00ky.create(id, payload);
|
|
1039
|
+
await this.sp00ky.create(id, unproxy(payload));
|
|
753
1040
|
}
|
|
754
1041
|
/**
|
|
755
1042
|
* Update an existing record in the database
|
|
756
1043
|
*/
|
|
757
1044
|
async update(tableName, recordId, payload, options) {
|
|
758
1045
|
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
759
|
-
await this.sp00ky.update(tableName, recordId, payload, options);
|
|
1046
|
+
await this.sp00ky.update(tableName, recordId, unproxy(payload), options);
|
|
760
1047
|
}
|
|
761
1048
|
/**
|
|
762
1049
|
* Delete an existing record in the database
|
|
763
1050
|
*/
|
|
764
1051
|
async delete(tableName, selector) {
|
|
765
1052
|
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
766
|
-
const
|
|
767
|
-
const
|
|
1053
|
+
const raw = unproxy(selector);
|
|
1054
|
+
const ctorName = raw?.constructor?.name;
|
|
1055
|
+
const isRecordId = raw instanceof RecordId || ctorName === "RecordId" || ctorName === "bound RecordId";
|
|
768
1056
|
let id;
|
|
769
|
-
if (typeof
|
|
770
|
-
else if (isRecordId) id = `${tableName}:${
|
|
1057
|
+
if (typeof raw === "string") id = raw;
|
|
1058
|
+
else if (isRecordId) id = `${tableName}:${raw.id}`;
|
|
771
1059
|
else throw new Error("Only string ID or RecordId selectors are supported currently with core");
|
|
772
1060
|
await this.sp00ky.delete(tableName, id);
|
|
773
1061
|
}
|
|
@@ -792,7 +1080,7 @@ var SyncedDb = class {
|
|
|
792
1080
|
*/
|
|
793
1081
|
async run(backend, path, payload, options) {
|
|
794
1082
|
if (!this.sp00ky) throw new Error("SyncedDb not initialized");
|
|
795
|
-
await this.sp00ky.run(backend, path, payload, options);
|
|
1083
|
+
await this.sp00ky.run(backend, path, unproxy(payload), options);
|
|
796
1084
|
}
|
|
797
1085
|
/**
|
|
798
1086
|
* Sign out, clear session and local storage
|
|
@@ -880,5 +1168,5 @@ var SyncedDb = class {
|
|
|
880
1168
|
};
|
|
881
1169
|
|
|
882
1170
|
//#endregion
|
|
883
|
-
export { RecordId, Sp00kyProvider, SyncedDb, Uuid, conflate, createPreload, createQuery, createSubmission, fromSubscription, useAppRelease, useCrdtField, useDb, useDownloadFile, useFeatureFlag, useFileUpload, usePendingMutations, useQuery, useStorageStatus, useSyncStatus };
|
|
1171
|
+
export { Blurhash, BucketImage, RecordId, Sp00kyProvider, SyncedDb, Uuid, conflate, createPreload, createQuery, createSubmission, fromSubscription, useAppRelease, useBlurhash, useBucketImage, useCrdtField, useDb, useDownloadFile, useFeatureFlag, useFileUpload, usePendingMutations, useQuery, useStorageStatus, useSyncStatus };
|
|
884
1172
|
//# sourceMappingURL=index.js.map
|