@arcote.tech/arc-adapter-db-postgres 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
@@ -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 context2 = this.#fn.buildContext(adapters);
3195
- if (adapters.authAdapter) {
3196
- const decoded = adapters.authAdapter.getDecoded();
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-postgres",
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",
@@ -23,7 +23,7 @@
23
23
  "postgres": "^3.4.4"
24
24
  },
25
25
  "peerDependencies": {
26
- "@arcote.tech/arc": "^0.5.1"
26
+ "@arcote.tech/arc": "^0.5.2"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/pg": "^8.11.0",