@arcote.tech/arc-adapter-db-sqlite-wasm 0.5.0 → 0.5.2
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/index.js +186 -58
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2439,6 +2439,109 @@ class ArcObject extends ArcAbstract {
|
|
|
2439
2439
|
return new ArcObject(partialShape);
|
|
2440
2440
|
}
|
|
2441
2441
|
}
|
|
2442
|
+
class DataStorage {
|
|
2443
|
+
async commitChanges(changes) {
|
|
2444
|
+
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
class ScopedStore {
|
|
2449
|
+
#inner;
|
|
2450
|
+
#restrictions;
|
|
2451
|
+
#canWrite;
|
|
2452
|
+
#storeName;
|
|
2453
|
+
constructor(inner, restrictions, canWrite) {
|
|
2454
|
+
this.#inner = inner;
|
|
2455
|
+
this.#restrictions = restrictions;
|
|
2456
|
+
this.#canWrite = canWrite;
|
|
2457
|
+
this.#storeName = inner.storeName;
|
|
2458
|
+
}
|
|
2459
|
+
get storeName() {
|
|
2460
|
+
return this.#storeName;
|
|
2461
|
+
}
|
|
2462
|
+
async find(options, listener) {
|
|
2463
|
+
const restricted = this.#applyReadRestrictions(options);
|
|
2464
|
+
return this.#inner.find(restricted, listener);
|
|
2465
|
+
}
|
|
2466
|
+
async set(item) {
|
|
2467
|
+
this.#assertWriteAccess();
|
|
2468
|
+
this.#validateScopeFields(item);
|
|
2469
|
+
return this.#inner.set(item);
|
|
2470
|
+
}
|
|
2471
|
+
async remove(id) {
|
|
2472
|
+
this.#assertWriteAccess();
|
|
2473
|
+
return this.#inner.remove(id);
|
|
2474
|
+
}
|
|
2475
|
+
async modify(id, data) {
|
|
2476
|
+
this.#assertWriteAccess();
|
|
2477
|
+
this.#validateScopeFields(data);
|
|
2478
|
+
return this.#inner.modify(id, data);
|
|
2479
|
+
}
|
|
2480
|
+
async applyChanges(changes) {
|
|
2481
|
+
this.#assertWriteAccess();
|
|
2482
|
+
for (const change of changes) {
|
|
2483
|
+
if (change.type === "set") {
|
|
2484
|
+
this.#validateScopeFields(change.data);
|
|
2485
|
+
} else if (change.type === "modify") {
|
|
2486
|
+
this.#validateScopeFields(change.data);
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
return this.#inner.applyChanges(changes);
|
|
2490
|
+
}
|
|
2491
|
+
unsubscribe(listener) {
|
|
2492
|
+
this.#inner.unsubscribe(listener);
|
|
2493
|
+
}
|
|
2494
|
+
#applyReadRestrictions(options) {
|
|
2495
|
+
if (Object.keys(this.#restrictions).length === 0) {
|
|
2496
|
+
return options ?? {};
|
|
2497
|
+
}
|
|
2498
|
+
return {
|
|
2499
|
+
...options,
|
|
2500
|
+
where: { ...options?.where ?? {}, ...this.#restrictions }
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
#assertWriteAccess() {
|
|
2504
|
+
if (!this.#canWrite) {
|
|
2505
|
+
throw new Error(`Scope violation: write access denied to store "${this.#storeName}" (read-only)`);
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
#validateScopeFields(data) {
|
|
2509
|
+
for (const [key, value] of Object.entries(this.#restrictions)) {
|
|
2510
|
+
if (key in data && data[key] !== value) {
|
|
2511
|
+
throw new Error(`Scope violation: field "${key}" must be "${value}", got "${data[key]}" in store "${this.#storeName}"`);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
class ScopedDataStorage extends DataStorage {
|
|
2518
|
+
#inner;
|
|
2519
|
+
#allowedStores;
|
|
2520
|
+
#restrictions;
|
|
2521
|
+
constructor(inner, allowedStores, restrictions) {
|
|
2522
|
+
super();
|
|
2523
|
+
this.#inner = inner;
|
|
2524
|
+
this.#allowedStores = allowedStores;
|
|
2525
|
+
this.#restrictions = restrictions;
|
|
2526
|
+
}
|
|
2527
|
+
getStore(storeName) {
|
|
2528
|
+
const permission = this.#allowedStores.get(storeName);
|
|
2529
|
+
if (!permission) {
|
|
2530
|
+
throw new Error(`Scope violation: access denied to store "${storeName}" (not declared in query/mutate)`);
|
|
2531
|
+
}
|
|
2532
|
+
const inner = this.#inner.getStore(storeName);
|
|
2533
|
+
return new ScopedStore(inner, this.#restrictions, permission === "read-write");
|
|
2534
|
+
}
|
|
2535
|
+
fork() {
|
|
2536
|
+
return this.#inner.fork();
|
|
2537
|
+
}
|
|
2538
|
+
getReadTransaction() {
|
|
2539
|
+
return this.#inner.getReadTransaction();
|
|
2540
|
+
}
|
|
2541
|
+
getReadWriteTransaction() {
|
|
2542
|
+
return this.#inner.getReadWriteTransaction();
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2442
2545
|
class ArcPrimitive extends ArcAbstract {
|
|
2443
2546
|
serialize(value) {
|
|
2444
2547
|
return value;
|
|
@@ -2623,6 +2726,54 @@ class ArcContextElement extends ArcFragmentBase {
|
|
|
2623
2726
|
return this._seeds;
|
|
2624
2727
|
}
|
|
2625
2728
|
}
|
|
2729
|
+
function buildElementContext(queryElements, mutationElements, adapters) {
|
|
2730
|
+
const queryMap = new Map;
|
|
2731
|
+
const mutateMap = new Map;
|
|
2732
|
+
const queryProps = {};
|
|
2733
|
+
for (const element of queryElements) {
|
|
2734
|
+
if (element.queryContext) {
|
|
2735
|
+
const ctx = element.queryContext(adapters);
|
|
2736
|
+
queryProps[element.name] = ctx;
|
|
2737
|
+
queryMap.set(element, ctx);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
const mutateProps = {};
|
|
2741
|
+
for (const element of mutationElements) {
|
|
2742
|
+
if (element.mutateContext) {
|
|
2743
|
+
const ctx = element.mutateContext(adapters);
|
|
2744
|
+
mutateProps[element.name] = ctx;
|
|
2745
|
+
mutateMap.set(element, ctx);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
const queryFn = (element) => {
|
|
2749
|
+
const cached = queryMap.get(element);
|
|
2750
|
+
if (cached)
|
|
2751
|
+
return cached;
|
|
2752
|
+
if (element.queryContext) {
|
|
2753
|
+
const ctx = element.queryContext(adapters);
|
|
2754
|
+
queryMap.set(element, ctx);
|
|
2755
|
+
return ctx;
|
|
2756
|
+
}
|
|
2757
|
+
throw new Error(`Element "${element.name}" has no queryContext`);
|
|
2758
|
+
};
|
|
2759
|
+
Object.assign(queryFn, queryProps);
|
|
2760
|
+
const mutateFn = (element) => {
|
|
2761
|
+
const cached = mutateMap.get(element);
|
|
2762
|
+
if (cached)
|
|
2763
|
+
return cached;
|
|
2764
|
+
if (element.mutateContext) {
|
|
2765
|
+
const ctx = element.mutateContext(adapters);
|
|
2766
|
+
mutateMap.set(element, ctx);
|
|
2767
|
+
return ctx;
|
|
2768
|
+
}
|
|
2769
|
+
throw new Error(`Element "${element.name}" has no mutateContext`);
|
|
2770
|
+
};
|
|
2771
|
+
Object.assign(mutateFn, mutateProps);
|
|
2772
|
+
return {
|
|
2773
|
+
query: queryFn,
|
|
2774
|
+
mutate: mutateFn
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2626
2777
|
|
|
2627
2778
|
class ArcBoolean extends ArcPrimitive {
|
|
2628
2779
|
hasToBeTrue() {
|
|
@@ -2826,55 +2977,6 @@ class ArcEvent extends ArcContextElement {
|
|
|
2826
2977
|
return ArcEvent.sharedDatabaseStoreSchema();
|
|
2827
2978
|
}
|
|
2828
2979
|
}
|
|
2829
|
-
function buildElementContext(queryElements, mutationElements, adapters) {
|
|
2830
|
-
const queryMap = new Map;
|
|
2831
|
-
const mutateMap = new Map;
|
|
2832
|
-
const queryProps = {};
|
|
2833
|
-
for (const element of queryElements) {
|
|
2834
|
-
if (element.queryContext) {
|
|
2835
|
-
const ctx = element.queryContext(adapters);
|
|
2836
|
-
queryProps[element.name] = ctx;
|
|
2837
|
-
queryMap.set(element, ctx);
|
|
2838
|
-
}
|
|
2839
|
-
}
|
|
2840
|
-
const mutateProps = {};
|
|
2841
|
-
for (const element of mutationElements) {
|
|
2842
|
-
if (element.mutateContext) {
|
|
2843
|
-
const ctx = element.mutateContext(adapters);
|
|
2844
|
-
mutateProps[element.name] = ctx;
|
|
2845
|
-
mutateMap.set(element, ctx);
|
|
2846
|
-
}
|
|
2847
|
-
}
|
|
2848
|
-
const queryFn = (element) => {
|
|
2849
|
-
const cached = queryMap.get(element);
|
|
2850
|
-
if (cached)
|
|
2851
|
-
return cached;
|
|
2852
|
-
if (element.queryContext) {
|
|
2853
|
-
const ctx = element.queryContext(adapters);
|
|
2854
|
-
queryMap.set(element, ctx);
|
|
2855
|
-
return ctx;
|
|
2856
|
-
}
|
|
2857
|
-
throw new Error(`Element "${element.name}" has no queryContext`);
|
|
2858
|
-
};
|
|
2859
|
-
Object.assign(queryFn, queryProps);
|
|
2860
|
-
const mutateFn = (element) => {
|
|
2861
|
-
const cached = mutateMap.get(element);
|
|
2862
|
-
if (cached)
|
|
2863
|
-
return cached;
|
|
2864
|
-
if (element.mutateContext) {
|
|
2865
|
-
const ctx = element.mutateContext(adapters);
|
|
2866
|
-
mutateMap.set(element, ctx);
|
|
2867
|
-
return ctx;
|
|
2868
|
-
}
|
|
2869
|
-
throw new Error(`Element "${element.name}" has no mutateContext`);
|
|
2870
|
-
};
|
|
2871
|
-
Object.assign(mutateFn, mutateProps);
|
|
2872
|
-
return {
|
|
2873
|
-
query: queryFn,
|
|
2874
|
-
mutate: mutateFn
|
|
2875
|
-
};
|
|
2876
|
-
}
|
|
2877
|
-
|
|
2878
2980
|
class ArcFunction {
|
|
2879
2981
|
data;
|
|
2880
2982
|
constructor(data) {
|
|
@@ -3191,9 +3293,10 @@ class ArcListener extends ArcContextElement {
|
|
|
3191
3293
|
async handleEvent(event2, adapters) {
|
|
3192
3294
|
if (!this.data.handler)
|
|
3193
3295
|
return;
|
|
3194
|
-
const
|
|
3195
|
-
|
|
3196
|
-
|
|
3296
|
+
const scopedAdapters = this.buildScopedAdapters(event2, adapters);
|
|
3297
|
+
const context2 = this.#fn.buildContext(scopedAdapters);
|
|
3298
|
+
if (scopedAdapters.authAdapter) {
|
|
3299
|
+
const decoded = scopedAdapters.authAdapter.getDecoded();
|
|
3197
3300
|
if (decoded) {
|
|
3198
3301
|
context2.$auth = {
|
|
3199
3302
|
params: decoded.params,
|
|
@@ -3209,6 +3312,37 @@ class ArcListener extends ArcContextElement {
|
|
|
3209
3312
|
await this.data.handler(context2, event2);
|
|
3210
3313
|
}
|
|
3211
3314
|
}
|
|
3315
|
+
buildScopedAdapters(event2, adapters) {
|
|
3316
|
+
if (adapters.authAdapter?.isAuthenticated()) {
|
|
3317
|
+
return adapters;
|
|
3318
|
+
}
|
|
3319
|
+
const allElements = [
|
|
3320
|
+
...this.data.queryElements,
|
|
3321
|
+
...this.data.mutationElements
|
|
3322
|
+
];
|
|
3323
|
+
let tokenName = null;
|
|
3324
|
+
for (const element of allElements) {
|
|
3325
|
+
const protections = element.__aggregateProtections ?? element.data?.protections;
|
|
3326
|
+
if (protections?.length > 0) {
|
|
3327
|
+
tokenName = protections[0].token.name;
|
|
3328
|
+
break;
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
if (!tokenName || !event2.payload) {
|
|
3332
|
+
return adapters;
|
|
3333
|
+
}
|
|
3334
|
+
const scopedAuth = new AuthAdapter;
|
|
3335
|
+
scopedAuth.scopes = new Map([
|
|
3336
|
+
["default", {
|
|
3337
|
+
raw: "",
|
|
3338
|
+
decoded: {
|
|
3339
|
+
tokenName,
|
|
3340
|
+
params: event2.payload
|
|
3341
|
+
}
|
|
3342
|
+
}]
|
|
3343
|
+
]);
|
|
3344
|
+
return { ...adapters, authAdapter: scopedAuth };
|
|
3345
|
+
}
|
|
3212
3346
|
destroy() {
|
|
3213
3347
|
for (const unsubscribe of this.unsubscribers) {
|
|
3214
3348
|
unsubscribe();
|
|
@@ -3313,12 +3447,6 @@ class ArcRoute extends ArcContextElement {
|
|
|
3313
3447
|
return context2;
|
|
3314
3448
|
}
|
|
3315
3449
|
}
|
|
3316
|
-
class DataStorage {
|
|
3317
|
-
async commitChanges(changes) {
|
|
3318
|
-
await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
|
|
3319
|
-
}
|
|
3320
|
-
}
|
|
3321
|
-
|
|
3322
3450
|
class StoreState {
|
|
3323
3451
|
storeName;
|
|
3324
3452
|
dataStorage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-adapter-db-sqlite-wasm",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dev": "bun build ./src/index.ts ./src/worker.ts --outdir ./dist --target browser --format esm --watch"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@arcote.tech/arc": "^0.5.
|
|
26
|
+
"@arcote.tech/arc": "^0.5.2",
|
|
27
27
|
"@sqlite.org/sqlite-wasm": "^3.46.0-build1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|