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