@arcote.tech/arc-adapter-db-sqlite 0.5.1 → 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.
Files changed (2) hide show
  1. package/dist/index.js +186 -58
  2. 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 context2 = this.#fn.buildContext(adapters);
3198
- if (adapters.authAdapter) {
3199
- const decoded = adapters.authAdapter.getDecoded();
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.1",
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.1"
23
+ "@arcote.tech/arc": "^0.5.2"
24
24
  },
25
25
  "devDependencies": {
26
26
  "typescript": "^5.0.0"