@arcote.tech/arc-adapter-db-sqlite 0.5.1 → 0.5.5

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 +188 -63
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1404,6 +1404,9 @@ class AuthAdapter {
1404
1404
  localStorage.removeItem(TOKEN_PREFIX + scope);
1405
1405
  }
1406
1406
  }
1407
+ setDecoded(decoded, scope = "default") {
1408
+ this.scopes.set(scope, { raw: "", decoded });
1409
+ }
1407
1410
  loadPersisted() {
1408
1411
  if (!hasLocalStorage())
1409
1412
  return;
@@ -1601,7 +1604,8 @@ class EventWire {
1601
1604
  localId: e.localId,
1602
1605
  type: e.type,
1603
1606
  payload: e.payload,
1604
- createdAt: e.createdAt
1607
+ createdAt: e.createdAt,
1608
+ authContext: e.authContext
1605
1609
  }))
1606
1610
  }));
1607
1611
  }
@@ -1758,11 +1762,13 @@ class LocalEventPublisher {
1758
1762
  }
1759
1763
  async publish(event) {
1760
1764
  const allChanges = [];
1765
+ const eventAuthContext = event.authContext ?? null;
1761
1766
  const storedEvent = {
1762
1767
  _id: event.id,
1763
1768
  type: event.type,
1764
1769
  payload: JSON.stringify(event.payload),
1765
- createdAt: event.createdAt.toISOString()
1770
+ createdAt: event.createdAt.toISOString(),
1771
+ authContext: eventAuthContext ? JSON.stringify(eventAuthContext) : null
1766
1772
  };
1767
1773
  allChanges.push({
1768
1774
  store: EVENT_TABLES.events,
@@ -2442,6 +2448,109 @@ class ArcObject extends ArcAbstract {
2442
2448
  return new ArcObject(partialShape);
2443
2449
  }
2444
2450
  }
2451
+ class DataStorage {
2452
+ async commitChanges(changes) {
2453
+ await Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
2454
+ }
2455
+ }
2456
+
2457
+ class ScopedStore {
2458
+ #inner;
2459
+ #restrictions;
2460
+ #canWrite;
2461
+ #storeName;
2462
+ constructor(inner, restrictions, canWrite) {
2463
+ this.#inner = inner;
2464
+ this.#restrictions = restrictions;
2465
+ this.#canWrite = canWrite;
2466
+ this.#storeName = inner.storeName;
2467
+ }
2468
+ get storeName() {
2469
+ return this.#storeName;
2470
+ }
2471
+ async find(options, listener) {
2472
+ const restricted = this.#applyReadRestrictions(options);
2473
+ return this.#inner.find(restricted, listener);
2474
+ }
2475
+ async set(item) {
2476
+ this.#assertWriteAccess();
2477
+ this.#validateScopeFields(item);
2478
+ return this.#inner.set(item);
2479
+ }
2480
+ async remove(id) {
2481
+ this.#assertWriteAccess();
2482
+ return this.#inner.remove(id);
2483
+ }
2484
+ async modify(id, data) {
2485
+ this.#assertWriteAccess();
2486
+ this.#validateScopeFields(data);
2487
+ return this.#inner.modify(id, data);
2488
+ }
2489
+ async applyChanges(changes) {
2490
+ this.#assertWriteAccess();
2491
+ for (const change of changes) {
2492
+ if (change.type === "set") {
2493
+ this.#validateScopeFields(change.data);
2494
+ } else if (change.type === "modify") {
2495
+ this.#validateScopeFields(change.data);
2496
+ }
2497
+ }
2498
+ return this.#inner.applyChanges(changes);
2499
+ }
2500
+ unsubscribe(listener) {
2501
+ this.#inner.unsubscribe(listener);
2502
+ }
2503
+ #applyReadRestrictions(options) {
2504
+ if (Object.keys(this.#restrictions).length === 0) {
2505
+ return options ?? {};
2506
+ }
2507
+ return {
2508
+ ...options,
2509
+ where: { ...options?.where ?? {}, ...this.#restrictions }
2510
+ };
2511
+ }
2512
+ #assertWriteAccess() {
2513
+ if (!this.#canWrite) {
2514
+ throw new Error(`Scope violation: write access denied to store "${this.#storeName}" (read-only)`);
2515
+ }
2516
+ }
2517
+ #validateScopeFields(data) {
2518
+ for (const [key, value] of Object.entries(this.#restrictions)) {
2519
+ if (key in data && data[key] !== value) {
2520
+ throw new Error(`Scope violation: field "${key}" must be "${value}", got "${data[key]}" in store "${this.#storeName}"`);
2521
+ }
2522
+ }
2523
+ }
2524
+ }
2525
+
2526
+ class ScopedDataStorage extends DataStorage {
2527
+ #inner;
2528
+ #allowedStores;
2529
+ #restrictions;
2530
+ constructor(inner, allowedStores, restrictions) {
2531
+ super();
2532
+ this.#inner = inner;
2533
+ this.#allowedStores = allowedStores;
2534
+ this.#restrictions = restrictions;
2535
+ }
2536
+ getStore(storeName) {
2537
+ const permission = this.#allowedStores.get(storeName);
2538
+ if (!permission) {
2539
+ throw new Error(`Scope violation: access denied to store "${storeName}" (not declared in query/mutate)`);
2540
+ }
2541
+ const inner = this.#inner.getStore(storeName);
2542
+ return new ScopedStore(inner, this.#restrictions, permission === "read-write");
2543
+ }
2544
+ fork() {
2545
+ return this.#inner.fork();
2546
+ }
2547
+ getReadTransaction() {
2548
+ return this.#inner.getReadTransaction();
2549
+ }
2550
+ getReadWriteTransaction() {
2551
+ return this.#inner.getReadWriteTransaction();
2552
+ }
2553
+ }
2445
2554
  class ArcPrimitive extends ArcAbstract {
2446
2555
  serialize(value) {
2447
2556
  return value;
@@ -2626,6 +2735,54 @@ class ArcContextElement extends ArcFragmentBase {
2626
2735
  return this._seeds;
2627
2736
  }
2628
2737
  }
2738
+ function buildElementContext(queryElements, mutationElements, adapters) {
2739
+ const queryMap = new Map;
2740
+ const mutateMap = new Map;
2741
+ const queryProps = {};
2742
+ for (const element of queryElements) {
2743
+ if (element.queryContext) {
2744
+ const ctx = element.queryContext(adapters);
2745
+ queryProps[element.name] = ctx;
2746
+ queryMap.set(element, ctx);
2747
+ }
2748
+ }
2749
+ const mutateProps = {};
2750
+ for (const element of mutationElements) {
2751
+ if (element.mutateContext) {
2752
+ const ctx = element.mutateContext(adapters);
2753
+ mutateProps[element.name] = ctx;
2754
+ mutateMap.set(element, ctx);
2755
+ }
2756
+ }
2757
+ const queryFn = (element) => {
2758
+ const cached = queryMap.get(element);
2759
+ if (cached)
2760
+ return cached;
2761
+ if (element.queryContext) {
2762
+ const ctx = element.queryContext(adapters);
2763
+ queryMap.set(element, ctx);
2764
+ return ctx;
2765
+ }
2766
+ throw new Error(`Element "${element.name}" has no queryContext`);
2767
+ };
2768
+ Object.assign(queryFn, queryProps);
2769
+ const mutateFn = (element) => {
2770
+ const cached = mutateMap.get(element);
2771
+ if (cached)
2772
+ return cached;
2773
+ if (element.mutateContext) {
2774
+ const ctx = element.mutateContext(adapters);
2775
+ mutateMap.set(element, ctx);
2776
+ return ctx;
2777
+ }
2778
+ throw new Error(`Element "${element.name}" has no mutateContext`);
2779
+ };
2780
+ Object.assign(mutateFn, mutateProps);
2781
+ return {
2782
+ query: queryFn,
2783
+ mutate: mutateFn
2784
+ };
2785
+ }
2629
2786
 
2630
2787
  class ArcBoolean extends ArcPrimitive {
2631
2788
  hasToBeTrue() {
@@ -2749,11 +2906,14 @@ class ArcEvent extends ArcContextElement {
2749
2906
  if (!adapters.eventPublisher) {
2750
2907
  throw new Error(`Event "${this.data.name}" cannot be emitted: no eventPublisher adapter available`);
2751
2908
  }
2909
+ const decoded = adapters.authAdapter?.getDecoded() ?? null;
2910
+ const authContext = decoded ? { tokenName: decoded.tokenName, params: decoded.params } : null;
2752
2911
  const event = {
2753
2912
  type: this.data.name,
2754
2913
  payload,
2755
2914
  id: this.eventId.generate(),
2756
- createdAt: new Date
2915
+ createdAt: new Date,
2916
+ authContext
2757
2917
  };
2758
2918
  await adapters.eventPublisher.publish(event);
2759
2919
  }
@@ -2829,55 +2989,6 @@ class ArcEvent extends ArcContextElement {
2829
2989
  return ArcEvent.sharedDatabaseStoreSchema();
2830
2990
  }
2831
2991
  }
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
2992
  class ArcFunction {
2882
2993
  data;
2883
2994
  constructor(data) {
@@ -2994,11 +3105,14 @@ class AggregateBase {
2994
3105
  if (!adapters.eventPublisher) {
2995
3106
  throw new Error(`Cannot emit event "${arcEvent.name}": no eventPublisher adapter available`);
2996
3107
  }
3108
+ const decoded = adapters.authAdapter?.getDecoded() ?? null;
3109
+ const authContext = decoded ? { tokenName: decoded.tokenName, params: decoded.params } : null;
2997
3110
  const eventInstance = {
2998
3111
  type: arcEvent.name,
2999
3112
  payload,
3000
3113
  id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
3001
- createdAt: new Date
3114
+ createdAt: new Date,
3115
+ authContext
3002
3116
  };
3003
3117
  await adapters.eventPublisher.publish(eventInstance);
3004
3118
  }
@@ -3194,9 +3308,10 @@ class ArcListener extends ArcContextElement {
3194
3308
  async handleEvent(event2, adapters) {
3195
3309
  if (!this.data.handler)
3196
3310
  return;
3197
- const context2 = this.#fn.buildContext(adapters);
3198
- if (adapters.authAdapter) {
3199
- const decoded = adapters.authAdapter.getDecoded();
3311
+ const scopedAdapters = this.buildScopedAdapters(event2, adapters);
3312
+ const context2 = this.#fn.buildContext(scopedAdapters);
3313
+ if (scopedAdapters.authAdapter) {
3314
+ const decoded = scopedAdapters.authAdapter.getDecoded();
3200
3315
  if (decoded) {
3201
3316
  context2.$auth = {
3202
3317
  params: decoded.params,
@@ -3212,6 +3327,21 @@ class ArcListener extends ArcContextElement {
3212
3327
  await this.data.handler(context2, event2);
3213
3328
  }
3214
3329
  }
3330
+ buildScopedAdapters(event2, adapters) {
3331
+ if (adapters.authAdapter?.isAuthenticated()) {
3332
+ return adapters;
3333
+ }
3334
+ const authContext = event2.authContext;
3335
+ if (!authContext) {
3336
+ return adapters;
3337
+ }
3338
+ const scopedAuth = new AuthAdapter;
3339
+ scopedAuth.setDecoded({
3340
+ tokenName: authContext.tokenName,
3341
+ params: authContext.params
3342
+ });
3343
+ return { ...adapters, authAdapter: scopedAuth };
3344
+ }
3215
3345
  destroy() {
3216
3346
  for (const unsubscribe of this.unsubscribers) {
3217
3347
  unsubscribe();
@@ -3316,12 +3446,6 @@ class ArcRoute extends ArcContextElement {
3316
3446
  return context2;
3317
3447
  }
3318
3448
  }
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
3449
  class StoreState {
3326
3450
  storeName;
3327
3451
  dataStorage;
@@ -4342,7 +4466,8 @@ class StreamingEventPublisher {
4342
4466
  localId: event3.id,
4343
4467
  type: event3.type,
4344
4468
  payload: event3.payload,
4345
- createdAt: event3.createdAt.toISOString()
4469
+ createdAt: event3.createdAt.toISOString(),
4470
+ authContext: event3.authContext ?? null
4346
4471
  }
4347
4472
  ]);
4348
4473
  }
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.5",
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.5"
24
24
  },
25
25
  "devDependencies": {
26
26
  "typescript": "^5.0.0"