@arcote.tech/arc-cli 0.7.27 → 0.7.28

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 CHANGED
@@ -9634,6 +9634,7 @@ var exports_dist2 = {};
9634
9634
  __export(exports_dist2, {
9635
9635
  createPostgreSQLAdapterFactoryFromUrl: () => createPostgreSQLAdapterFactoryFromUrl,
9636
9636
  createPostgreSQLAdapterFactory: () => createPostgreSQLAdapterFactory,
9637
+ coercePgNumber: () => coercePgNumber,
9637
9638
  PostgreSQLAdapter: () => PostgreSQLAdapter
9638
9639
  });
9639
9640
  import os2 from "os";
@@ -10866,9 +10867,21 @@ class LocalEventPublisher2 {
10866
10867
  views = [];
10867
10868
  syncCallback;
10868
10869
  subscribers = new Map;
10870
+ pending = new Set;
10869
10871
  constructor(dataStorage) {
10870
10872
  this.dataStorage = dataStorage;
10871
10873
  }
10874
+ track(work) {
10875
+ const wrapped = Promise.resolve(work).catch(() => {}).finally(() => {
10876
+ this.pending.delete(wrapped);
10877
+ });
10878
+ this.pending.add(wrapped);
10879
+ }
10880
+ async whenIdle() {
10881
+ while (this.pending.size > 0) {
10882
+ await Promise.all([...this.pending]);
10883
+ }
10884
+ }
10872
10885
  onPublish(callback) {
10873
10886
  this.syncCallback = callback;
10874
10887
  }
@@ -10904,9 +10917,12 @@ class LocalEventPublisher2 {
10904
10917
  store: EVENT_TABLES.events,
10905
10918
  changes: [{ type: "set", data: storedEvent }]
10906
10919
  });
10907
- if (event.payload && typeof event.payload === "object") {
10920
+ const tagFields = event.definition?.tagFields ?? [];
10921
+ if (tagFields.length > 0 && event.payload && typeof event.payload === "object") {
10922
+ const payload = event.payload;
10908
10923
  const tagChanges = [];
10909
- for (const [key, value] of Object.entries(event.payload)) {
10924
+ for (const key of tagFields) {
10925
+ const value = payload[key];
10910
10926
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
10911
10927
  tagChanges.push({
10912
10928
  type: "set",
@@ -11064,6 +11080,47 @@ class LocalEventPublisher2 {
11064
11080
  return allViewChanges;
11065
11081
  }
11066
11082
  }
11083
+ function murmurHash(key, seed = 0) {
11084
+ let remainder, bytes, h1, h1b, c1, c2, k1, i;
11085
+ remainder = key.length & 3;
11086
+ bytes = key.length - remainder;
11087
+ h1 = seed;
11088
+ c1 = 3432918353;
11089
+ c2 = 461845907;
11090
+ i = 0;
11091
+ while (i < bytes) {
11092
+ k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
11093
+ ++i;
11094
+ k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
11095
+ k1 = k1 << 15 | k1 >>> 17;
11096
+ k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
11097
+ h1 ^= k1;
11098
+ h1 = h1 << 13 | h1 >>> 19;
11099
+ h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
11100
+ h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
11101
+ }
11102
+ k1 = 0;
11103
+ if (remainder >= 3) {
11104
+ k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
11105
+ }
11106
+ if (remainder >= 2) {
11107
+ k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
11108
+ }
11109
+ if (remainder >= 1) {
11110
+ k1 ^= key.charCodeAt(i) & 255;
11111
+ k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
11112
+ k1 = k1 << 15 | k1 >>> 17;
11113
+ k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
11114
+ h1 ^= k1;
11115
+ }
11116
+ h1 ^= key.length;
11117
+ h1 ^= h1 >>> 16;
11118
+ h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
11119
+ h1 ^= h1 >>> 13;
11120
+ h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
11121
+ h1 ^= h1 >>> 16;
11122
+ return h1 >>> 0;
11123
+ }
11067
11124
 
11068
11125
  class ArcOptional {
11069
11126
  parent;
@@ -11156,7 +11213,9 @@ class ArcDefault {
11156
11213
  this.defaultValueOrCallback = defaultValueOrCallback;
11157
11214
  }
11158
11215
  validate(value) {
11159
- throw new Error("Method not implemented.");
11216
+ if (value === undefined || value === null)
11217
+ return false;
11218
+ return this.parent.validate(value);
11160
11219
  }
11161
11220
  parse(value) {
11162
11221
  if (value)
@@ -11393,6 +11452,25 @@ class ScopedStore {
11393
11452
  }
11394
11453
  }
11395
11454
  }
11455
+ function collectFieldPaths(errors, prefix = "") {
11456
+ if (!errors || typeof errors !== "object")
11457
+ return [];
11458
+ const schema = errors.schema;
11459
+ if (!schema || typeof schema !== "object")
11460
+ return [];
11461
+ const out = [];
11462
+ for (const [field, fieldError] of Object.entries(schema)) {
11463
+ if (!fieldError)
11464
+ continue;
11465
+ const path4 = prefix ? `${prefix}.${field}` : field;
11466
+ const nested = collectFieldPaths(fieldError, path4);
11467
+ if (nested.length > 0)
11468
+ out.push(...nested);
11469
+ else
11470
+ out.push(path4);
11471
+ }
11472
+ return out;
11473
+ }
11396
11474
  function string() {
11397
11475
  return new ArcString;
11398
11476
  }
@@ -11527,6 +11605,27 @@ class ArcFunction {
11527
11605
  get results() {
11528
11606
  return this.data.results;
11529
11607
  }
11608
+ validateParams(params) {
11609
+ const schema = this.data.params;
11610
+ if (!schema || typeof schema.validate !== "function")
11611
+ return;
11612
+ const errors = schema.validate(params ?? {});
11613
+ if (errors)
11614
+ throw new ArcValidationError2(errors);
11615
+ }
11616
+ async authorize(adapters) {
11617
+ if (!this.hasProtections)
11618
+ return;
11619
+ const decoded = adapters.authAdapter?.getDecoded?.();
11620
+ if (!decoded) {
11621
+ throw new ArcAuthenticationError;
11622
+ }
11623
+ const instances = this.data.protections.filter((p3) => p3.token?.name === decoded.tokenName).map((p3) => p3.token.create(decoded.params, adapters));
11624
+ const ok2 = await this.verifyProtections(instances);
11625
+ if (!ok2) {
11626
+ throw new ArcAuthorizationError2;
11627
+ }
11628
+ }
11530
11629
  async verifyProtections(tokens) {
11531
11630
  if (!this.data.protections || this.data.protections.length === 0) {
11532
11631
  return true;
@@ -11678,47 +11777,6 @@ function deepMerge(target, source) {
11678
11777
  function isPlainObject(item) {
11679
11778
  return item && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && Object.prototype.toString.call(item) === "[object Object]";
11680
11779
  }
11681
- function murmurHash(key, seed = 0) {
11682
- let remainder, bytes, h1, h1b, c1, c2, k1, i;
11683
- remainder = key.length & 3;
11684
- bytes = key.length - remainder;
11685
- h1 = seed;
11686
- c1 = 3432918353;
11687
- c2 = 461845907;
11688
- i = 0;
11689
- while (i < bytes) {
11690
- k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
11691
- ++i;
11692
- k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
11693
- k1 = k1 << 15 | k1 >>> 17;
11694
- k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
11695
- h1 ^= k1;
11696
- h1 = h1 << 13 | h1 >>> 19;
11697
- h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
11698
- h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
11699
- }
11700
- k1 = 0;
11701
- if (remainder >= 3) {
11702
- k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
11703
- }
11704
- if (remainder >= 2) {
11705
- k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
11706
- }
11707
- if (remainder >= 1) {
11708
- k1 ^= key.charCodeAt(i) & 255;
11709
- k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
11710
- k1 = k1 << 15 | k1 >>> 17;
11711
- k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
11712
- h1 ^= k1;
11713
- }
11714
- h1 ^= key.length;
11715
- h1 ^= h1 >>> 16;
11716
- h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
11717
- h1 ^= h1 >>> 13;
11718
- h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
11719
- h1 ^= h1 >>> 16;
11720
- return h1 >>> 0;
11721
- }
11722
11780
  function resolveQueryChange(currentResult, event3, options) {
11723
11781
  const index = currentResult.findIndex((e2) => e2._id === event3.id);
11724
11782
  const isInCurrentResult = index !== -1;
@@ -11730,16 +11788,57 @@ function resolveQueryChange(currentResult, event3, options) {
11730
11788
  }
11731
11789
  const shouldBeInResult = checkItemMatchesWhere(event3.item, options.where);
11732
11790
  if (isInCurrentResult && shouldBeInResult) {
11733
- const newResult = currentResult.toSpliced(index, 1, event3.item);
11734
- return applyOrderByAndLimit(newResult, options);
11791
+ if (!options.orderBy) {
11792
+ return applyLimit(currentResult.toSpliced(index, 1, event3.item), options);
11793
+ }
11794
+ const cmp = compareByOrderBy(options.orderBy);
11795
+ if (cmp(currentResult[index], event3.item) === 0) {
11796
+ return applyLimit(currentResult.toSpliced(index, 1, event3.item), options);
11797
+ }
11798
+ const without = currentResult.toSpliced(index, 1);
11799
+ const at = upperBound(without, event3.item, cmp);
11800
+ return applyLimit(without.toSpliced(at, 0, event3.item), options);
11735
11801
  } else if (isInCurrentResult && !shouldBeInResult) {
11736
11802
  return currentResult.toSpliced(index, 1);
11737
11803
  } else if (!isInCurrentResult && shouldBeInResult) {
11738
- const newResult = [...currentResult, event3.item];
11739
- return applyOrderByAndLimit(newResult, options);
11804
+ if (!options.orderBy) {
11805
+ return applyLimit([...currentResult, event3.item], options);
11806
+ }
11807
+ const cmp = compareByOrderBy(options.orderBy);
11808
+ const at = upperBound(currentResult, event3.item, cmp);
11809
+ return applyLimit(currentResult.toSpliced(at, 0, event3.item), options);
11740
11810
  }
11741
11811
  return false;
11742
11812
  }
11813
+ function compareByOrderBy(orderBy) {
11814
+ const entries = Object.entries(orderBy);
11815
+ return (a2, b3) => {
11816
+ for (const [key, direction] of entries) {
11817
+ const aVal = a2[key];
11818
+ const bVal = b3[key];
11819
+ if (aVal < bVal)
11820
+ return direction === "asc" ? -1 : 1;
11821
+ if (aVal > bVal)
11822
+ return direction === "asc" ? 1 : -1;
11823
+ }
11824
+ return 0;
11825
+ };
11826
+ }
11827
+ function upperBound(arr, item, cmp) {
11828
+ let lo = 0;
11829
+ let hi = arr.length;
11830
+ while (lo < hi) {
11831
+ const mid = lo + hi >> 1;
11832
+ if (cmp(arr[mid], item) <= 0)
11833
+ lo = mid + 1;
11834
+ else
11835
+ hi = mid;
11836
+ }
11837
+ return lo;
11838
+ }
11839
+ function applyLimit(result, options) {
11840
+ return options.limit !== undefined && result.length > options.limit ? result.slice(0, options.limit) : result;
11841
+ }
11743
11842
  function checkItemMatchesWhere(item, where) {
11744
11843
  if (!where) {
11745
11844
  return true;
@@ -11775,26 +11874,6 @@ function checkItemMatchesWhere(item, where) {
11775
11874
  });
11776
11875
  });
11777
11876
  }
11778
- function applyOrderByAndLimit(result, options) {
11779
- let sorted = result;
11780
- if (options.orderBy) {
11781
- sorted = [...result].sort((a2, b3) => {
11782
- for (const [key, direction] of Object.entries(options.orderBy)) {
11783
- const aVal = a2[key];
11784
- const bVal = b3[key];
11785
- if (aVal < bVal)
11786
- return direction === "asc" ? -1 : 1;
11787
- if (aVal > bVal)
11788
- return direction === "asc" ? 1 : -1;
11789
- }
11790
- return 0;
11791
- });
11792
- }
11793
- if (options.limit !== undefined) {
11794
- sorted = sorted.slice(0, options.limit);
11795
- }
11796
- return sorted;
11797
- }
11798
11877
 
11799
11878
  class ObservableDataStorage {
11800
11879
  source;
@@ -12131,7 +12210,7 @@ class Model2 {
12131
12210
  return s;
12132
12211
  }
12133
12212
  }
12134
- function applyQueryChanges(result, changes) {
12213
+ function applyQueryChanges2(result, changes) {
12135
12214
  const next = [...result];
12136
12215
  for (const change of changes) {
12137
12216
  if (change.type === "delete") {
@@ -12184,7 +12263,7 @@ class StreamingQueryCache {
12184
12263
  if (!newEntry.hasResult || !Array.isArray(newEntry.result)) {
12185
12264
  return;
12186
12265
  }
12187
- newEntry.result = applyQueryChanges(newEntry.result, changes);
12266
+ newEntry.result = applyQueryChanges2(newEntry.result, changes);
12188
12267
  this.notify(newEntry);
12189
12268
  }
12190
12269
  });
@@ -14049,6 +14128,37 @@ function osUsername() {
14049
14128
  return process.env.USERNAME || process.env.USER || process.env.LOGNAME;
14050
14129
  }
14051
14130
  }
14131
+ function unsafeIntMessage(raw, columnName) {
14132
+ const where = columnName ? ` w kolumnie "${columnName}"` : "";
14133
+ return `[PostgreSQL] Warto\u015B\u0107 ca\u0142kowita ${raw}${where} przekracza bezpieczny zakres ` + `JS Number (\xB12^53-1). Odczyt jako number cicho straci\u0142by precyzj\u0119. ` + `U\u017Cyj kolumny TEXT/NUMERIC i obs\u0142u\u017C warto\u015B\u0107 jako string/BigInt.`;
14134
+ }
14135
+ function coercePgNumber(value, columnName) {
14136
+ if (value === null || value === undefined)
14137
+ return value;
14138
+ if (typeof value === "number")
14139
+ return value;
14140
+ if (typeof value === "bigint") {
14141
+ if (value > MAX_SAFE_INT || value < MIN_SAFE_INT) {
14142
+ throw new Error(unsafeIntMessage(value.toString(), columnName));
14143
+ }
14144
+ return Number(value);
14145
+ }
14146
+ if (typeof value === "string") {
14147
+ const s = value.trim();
14148
+ if (s === "")
14149
+ return value;
14150
+ if (/^[+-]?\d+$/.test(s)) {
14151
+ const big = BigInt(s);
14152
+ if (big > MAX_SAFE_INT || big < MIN_SAFE_INT) {
14153
+ throw new Error(unsafeIntMessage(s, columnName));
14154
+ }
14155
+ return Number(big);
14156
+ }
14157
+ const n2 = Number(s);
14158
+ return Number.isNaN(n2) ? value : n2;
14159
+ }
14160
+ return value;
14161
+ }
14052
14162
 
14053
14163
  class PostgreSQLReadTransaction {
14054
14164
  db;
@@ -14071,7 +14181,27 @@ class PostgreSQLReadTransaction {
14071
14181
  deserializeValue(value, column) {
14072
14182
  if (value === null || value === undefined)
14073
14183
  return null;
14074
- switch (column.type.toLowerCase()) {
14184
+ const type = column.type.toLowerCase().split("(")[0].trim();
14185
+ switch (type) {
14186
+ case "bigint":
14187
+ case "int8":
14188
+ case "bigserial":
14189
+ case "serial8":
14190
+ case "serial":
14191
+ case "serial4":
14192
+ case "integer":
14193
+ case "int":
14194
+ case "int4":
14195
+ case "smallint":
14196
+ case "int2":
14197
+ case "numeric":
14198
+ case "decimal":
14199
+ case "real":
14200
+ case "double precision":
14201
+ case "float":
14202
+ case "float4":
14203
+ case "float8":
14204
+ return coercePgNumber(value, column.name);
14075
14205
  case "json":
14076
14206
  case "jsonb":
14077
14207
  if (typeof value === "string") {
@@ -14667,7 +14797,7 @@ var Operation, PROXY_DRAFT, RAW_RETURN_SYMBOL, iteratorSymbol, dataTypes, intern
14667
14797
  }
14668
14798
  return returnValue(result);
14669
14799
  };
14670
- }, create, constructorString, TOKEN_PREFIX = "arc:token:", DEFAULT_TIMEOUT_MS = 8000, provider = null, latestSync = null, syncSeq = 0, eventWireInstanceCounter = 0, EVENT_TABLES, arrayValidator, ArcArray, objectValidator, ArcObject, ScopedDataStorage, ArcPrimitive, stringValidator, ArcString, ArcContextElement, ArcBoolean, ArcId, numberValidator, ArcNumber, ArcEvent, ArcCommand, ArcListener, ArcRoute, ForkedStoreState, ForkedDataStorage, MasterStoreState, MasterDataStorage2, dateValidator, originCache, originStackCache, originError, CLOSE, Query, PostgresError, Errors, types2, Identifier, Parameter, Builder, defaultHandlers, builders, serializers, parsers, mergeUserTypes = function(types22) {
14800
+ }, create, constructorString, TOKEN_PREFIX = "arc:token:", DEFAULT_TIMEOUT_MS = 8000, provider = null, latestSync = null, syncSeq = 0, eventWireInstanceCounter = 0, EVENT_TABLES, arrayValidator, ArcArray, objectValidator, ArcObject, ScopedDataStorage, ArcAuthenticationError, ArcAuthorizationError2, ArcValidationError2, ArcPrimitive, stringValidator, ArcString, ArcContextElement, ArcBoolean, ArcId, numberValidator, ArcNumber, ArcEvent, ArcCommand, ArcListener, ArcRoute, ForkedStoreState, ForkedDataStorage, MasterStoreState, MasterDataStorage2, dateValidator, _te, _td, originCache, originStackCache, originError, CLOSE, Query, PostgresError, Errors, types2, Identifier, Parameter, Builder, defaultHandlers, builders, serializers, parsers, mergeUserTypes = function(types22) {
14671
14801
  const user = typeHandlers(types22 || {});
14672
14802
  return {
14673
14803
  serializers: Object.assign({}, serializers, user.serializers),
@@ -14707,7 +14837,7 @@ var Operation, PROXY_DRAFT, RAW_RETURN_SYMBOL, iteratorSymbol, dataTypes, intern
14707
14837
  for (let i = 1;i < x2.length; i++)
14708
14838
  str += x2[i] === "_" ? x2[++i].toUpperCase() : x2[i];
14709
14839
  return str;
14710
- }, toKebab = (x2) => x2.replace(/_/g, "-"), fromCamel = (x2) => x2.replace(/([A-Z])/g, "_$1").toLowerCase(), fromPascal = (x2) => (x2.slice(0, 1) + x2.slice(1).replace(/([A-Z])/g, "_$1")).toLowerCase(), fromKebab = (x2) => x2.replace(/-/g, "_"), camel, pascal, kebab, Result, queue_default, size = 256, buffer, messages, b3, bytes_default, connection_default, uid = 1, Sync, Flush, SSLRequest, ExecuteUnnamed, DescribeUnnamed, noop = () => {}, retryRoutines, errorFields, noop2 = () => {}, src_default, PostgreSQLReadWriteTransaction, createPostgreSQLAdapterFactory = (db) => {
14840
+ }, toKebab = (x2) => x2.replace(/_/g, "-"), fromCamel = (x2) => x2.replace(/([A-Z])/g, "_$1").toLowerCase(), fromPascal = (x2) => (x2.slice(0, 1) + x2.slice(1).replace(/([A-Z])/g, "_$1")).toLowerCase(), fromKebab = (x2) => x2.replace(/-/g, "_"), camel, pascal, kebab, Result, queue_default, size = 256, buffer, messages, b3, bytes_default, connection_default, uid = 1, Sync, Flush, SSLRequest, ExecuteUnnamed, DescribeUnnamed, noop = () => {}, retryRoutines, errorFields, noop2 = () => {}, src_default, MAX_SAFE_INT, MIN_SAFE_INT, PostgreSQLReadWriteTransaction, createPostgreSQLAdapterFactory = (db) => {
14711
14841
  return async (context) => {
14712
14842
  const adapter = new PostgreSQLAdapter(db, context);
14713
14843
  await adapter.initialize();
@@ -14715,7 +14845,15 @@ var Operation, PROXY_DRAFT, RAW_RETURN_SYMBOL, iteratorSymbol, dataTypes, intern
14715
14845
  };
14716
14846
  }, createPostgreSQLAdapterFactoryFromUrl = (connectionString) => {
14717
14847
  const sql = src_default(connectionString, {
14718
- onnotice: () => {}
14848
+ onnotice: () => {},
14849
+ types: {
14850
+ bigint: {
14851
+ to: 20,
14852
+ from: [20],
14853
+ serialize: (v3) => v3.toString(),
14854
+ parse: (v3) => coercePgNumber(v3)
14855
+ }
14856
+ }
14719
14857
  });
14720
14858
  const db = new PostgresJsDatabase(sql);
14721
14859
  return createPostgreSQLAdapterFactory(db);
@@ -15416,6 +15554,30 @@ var init_dist = __esm(() => {
15416
15554
  return this.#inner.getReadWriteTransaction();
15417
15555
  }
15418
15556
  };
15557
+ ArcAuthenticationError = class ArcAuthenticationError extends Error {
15558
+ arcCode = "AUTHENTICATION";
15559
+ constructor(message = "Authentication required") {
15560
+ super(message);
15561
+ this.name = "ArcAuthenticationError";
15562
+ }
15563
+ };
15564
+ ArcAuthorizationError2 = class ArcAuthorizationError2 extends Error {
15565
+ arcCode = "AUTHORIZATION";
15566
+ constructor(message = "Forbidden") {
15567
+ super(message);
15568
+ this.name = "ArcAuthorizationError";
15569
+ }
15570
+ };
15571
+ ArcValidationError2 = class ArcValidationError2 extends Error {
15572
+ arcCode = "VALIDATION";
15573
+ fields;
15574
+ constructor(errors, message = "Invalid parameters") {
15575
+ super(message);
15576
+ this.name = "ArcValidationError";
15577
+ const paths = collectFieldPaths(errors);
15578
+ this.fields = paths.length > 0 ? paths : errors && typeof errors === "object" ? Object.keys(errors) : [];
15579
+ }
15580
+ };
15419
15581
  ArcPrimitive = class ArcPrimitive extends ArcAbstract {
15420
15582
  serialize(value) {
15421
15583
  return value;
@@ -15577,6 +15739,8 @@ var init_dist = __esm(() => {
15577
15739
  ArcContextElement = class ArcContextElement extends ArcFragmentBase {
15578
15740
  name;
15579
15741
  types = ["context-element"];
15742
+ __contextId;
15743
+ __contextName;
15580
15744
  get id() {
15581
15745
  return this.name;
15582
15746
  }
@@ -15714,7 +15878,8 @@ var init_dist = __esm(() => {
15714
15878
  payload,
15715
15879
  id: this.eventId.generate(),
15716
15880
  createdAt: new Date,
15717
- authContext
15881
+ authContext,
15882
+ definition: this
15718
15883
  };
15719
15884
  await adapters.eventPublisher.publish(event);
15720
15885
  }
@@ -15767,8 +15932,8 @@ var init_dist = __esm(() => {
15767
15932
  const tagsSchema = new ArcObject({
15768
15933
  _id: new ArcString().primaryKey(),
15769
15934
  eventId: new ArcString().index(),
15770
- tagKey: new ArcString().index(),
15771
- tagValue: new ArcString().index()
15935
+ tagKey: new ArcString,
15936
+ tagValue: new ArcString
15772
15937
  });
15773
15938
  const syncStatusSchema = new ArcObject({
15774
15939
  _id: new ArcString().primaryKey(),
@@ -15884,6 +16049,8 @@ var init_dist = __esm(() => {
15884
16049
  if (!this.data.handler) {
15885
16050
  throw new Error(`Command "${this.data.name}" has no handler`);
15886
16051
  }
16052
+ this.#fn.validateParams(params);
16053
+ await this.#fn.authorize(adapters);
15887
16054
  const context2 = this.buildCommandContext(adapters);
15888
16055
  return await this.data.handler(context2, params);
15889
16056
  }
@@ -15994,9 +16161,10 @@ var init_dist = __esm(() => {
15994
16161
  }
15995
16162
  }
15996
16163
  if (this.data.isAsync) {
15997
- Promise.resolve(this.data.handler(context2, event2)).catch((error) => {
16164
+ const work = Promise.resolve(this.data.handler(context2, event2)).catch((error) => {
15998
16165
  console.error(`Async listener "${this.data.name}" error:`, error);
15999
16166
  });
16167
+ adapters.eventPublisher?.track?.(work);
16000
16168
  } else {
16001
16169
  await this.data.handler(context2, event2);
16002
16170
  }
@@ -16450,6 +16618,8 @@ var init_dist = __esm(() => {
16450
16618
  }
16451
16619
  };
16452
16620
  dateValidator = typeValidatorBuilder("Date", (value) => value instanceof Date);
16621
+ _te = new TextEncoder;
16622
+ _td = new TextDecoder;
16453
16623
  originCache = new Map;
16454
16624
  originStackCache = new Map;
16455
16625
  originError = Symbol("OriginError");
@@ -16821,6 +16991,8 @@ var init_dist = __esm(() => {
16821
16991
  }
16822
16992
  });
16823
16993
  src_default = Postgres;
16994
+ MAX_SAFE_INT = BigInt(Number.MAX_SAFE_INTEGER);
16995
+ MIN_SAFE_INT = BigInt(Number.MIN_SAFE_INTEGER);
16824
16996
  PostgreSQLReadWriteTransaction = class PostgreSQLReadWriteTransaction extends PostgreSQLReadTransaction {
16825
16997
  adapter;
16826
16998
  queries = [];
@@ -18237,9 +18409,21 @@ class LocalEventPublisher3 {
18237
18409
  views = [];
18238
18410
  syncCallback;
18239
18411
  subscribers = new Map;
18412
+ pending = new Set;
18240
18413
  constructor(dataStorage) {
18241
18414
  this.dataStorage = dataStorage;
18242
18415
  }
18416
+ track(work) {
18417
+ const wrapped = Promise.resolve(work).catch(() => {}).finally(() => {
18418
+ this.pending.delete(wrapped);
18419
+ });
18420
+ this.pending.add(wrapped);
18421
+ }
18422
+ async whenIdle() {
18423
+ while (this.pending.size > 0) {
18424
+ await Promise.all([...this.pending]);
18425
+ }
18426
+ }
18243
18427
  onPublish(callback) {
18244
18428
  this.syncCallback = callback;
18245
18429
  }
@@ -18435,6 +18619,47 @@ class LocalEventPublisher3 {
18435
18619
  return allViewChanges;
18436
18620
  }
18437
18621
  }
18622
+ function murmurHash2(key, seed = 0) {
18623
+ let remainder, bytes, h1, h1b, c1, c2, k1, i;
18624
+ remainder = key.length & 3;
18625
+ bytes = key.length - remainder;
18626
+ h1 = seed;
18627
+ c1 = 3432918353;
18628
+ c2 = 461845907;
18629
+ i = 0;
18630
+ while (i < bytes) {
18631
+ k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
18632
+ ++i;
18633
+ k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
18634
+ k1 = k1 << 15 | k1 >>> 17;
18635
+ k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
18636
+ h1 ^= k1;
18637
+ h1 = h1 << 13 | h1 >>> 19;
18638
+ h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
18639
+ h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
18640
+ }
18641
+ k1 = 0;
18642
+ if (remainder >= 3) {
18643
+ k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
18644
+ }
18645
+ if (remainder >= 2) {
18646
+ k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
18647
+ }
18648
+ if (remainder >= 1) {
18649
+ k1 ^= key.charCodeAt(i) & 255;
18650
+ k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
18651
+ k1 = k1 << 15 | k1 >>> 17;
18652
+ k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
18653
+ h1 ^= k1;
18654
+ }
18655
+ h1 ^= key.length;
18656
+ h1 ^= h1 >>> 16;
18657
+ h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
18658
+ h1 ^= h1 >>> 13;
18659
+ h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
18660
+ h1 ^= h1 >>> 16;
18661
+ return h1 >>> 0;
18662
+ }
18438
18663
 
18439
18664
  class ArcOptional2 {
18440
18665
  parent;
@@ -18527,7 +18752,9 @@ class ArcDefault2 {
18527
18752
  this.defaultValueOrCallback = defaultValueOrCallback;
18528
18753
  }
18529
18754
  validate(value) {
18530
- throw new Error("Method not implemented.");
18755
+ if (value === undefined || value === null)
18756
+ return false;
18757
+ return this.parent.validate(value);
18531
18758
  }
18532
18759
  parse(value) {
18533
18760
  if (value)
@@ -18898,6 +19125,27 @@ class ArcFunction2 {
18898
19125
  get results() {
18899
19126
  return this.data.results;
18900
19127
  }
19128
+ validateParams(params) {
19129
+ const schema = this.data.params;
19130
+ if (!schema || typeof schema.validate !== "function")
19131
+ return;
19132
+ const errors = schema.validate(params ?? {});
19133
+ if (errors)
19134
+ throw new ArcValidationError3(errors);
19135
+ }
19136
+ async authorize(adapters) {
19137
+ if (!this.hasProtections)
19138
+ return;
19139
+ const decoded = adapters.authAdapter?.getDecoded?.();
19140
+ if (!decoded) {
19141
+ throw new ArcAuthenticationError2;
19142
+ }
19143
+ const instances = this.data.protections.filter((p3) => p3.token?.name === decoded.tokenName).map((p3) => p3.token.create(decoded.params, adapters));
19144
+ const ok2 = await this.verifyProtections(instances);
19145
+ if (!ok2) {
19146
+ throw new ArcAuthorizationError3;
19147
+ }
19148
+ }
18901
19149
  async verifyProtections(tokens) {
18902
19150
  if (!this.data.protections || this.data.protections.length === 0) {
18903
19151
  return true;
@@ -19049,47 +19297,6 @@ function deepMerge2(target, source) {
19049
19297
  function isPlainObject2(item) {
19050
19298
  return item && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date) && Object.prototype.toString.call(item) === "[object Object]";
19051
19299
  }
19052
- function murmurHash2(key, seed = 0) {
19053
- let remainder, bytes, h1, h1b, c1, c2, k1, i;
19054
- remainder = key.length & 3;
19055
- bytes = key.length - remainder;
19056
- h1 = seed;
19057
- c1 = 3432918353;
19058
- c2 = 461845907;
19059
- i = 0;
19060
- while (i < bytes) {
19061
- k1 = key.charCodeAt(i) & 255 | (key.charCodeAt(++i) & 255) << 8 | (key.charCodeAt(++i) & 255) << 16 | (key.charCodeAt(++i) & 255) << 24;
19062
- ++i;
19063
- k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
19064
- k1 = k1 << 15 | k1 >>> 17;
19065
- k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
19066
- h1 ^= k1;
19067
- h1 = h1 << 13 | h1 >>> 19;
19068
- h1b = (h1 & 65535) * 5 + (((h1 >>> 16) * 5 & 65535) << 16) & 4294967295;
19069
- h1 = (h1b & 65535) + 27492 + (((h1b >>> 16) + 58964 & 65535) << 16);
19070
- }
19071
- k1 = 0;
19072
- if (remainder >= 3) {
19073
- k1 ^= (key.charCodeAt(i + 2) & 255) << 16;
19074
- }
19075
- if (remainder >= 2) {
19076
- k1 ^= (key.charCodeAt(i + 1) & 255) << 8;
19077
- }
19078
- if (remainder >= 1) {
19079
- k1 ^= key.charCodeAt(i) & 255;
19080
- k1 = (k1 & 65535) * c1 + (((k1 >>> 16) * c1 & 65535) << 16) & 4294967295;
19081
- k1 = k1 << 15 | k1 >>> 17;
19082
- k1 = (k1 & 65535) * c2 + (((k1 >>> 16) * c2 & 65535) << 16) & 4294967295;
19083
- h1 ^= k1;
19084
- }
19085
- h1 ^= key.length;
19086
- h1 ^= h1 >>> 16;
19087
- h1 = (h1 & 65535) * 2246822507 + (((h1 >>> 16) * 2246822507 & 65535) << 16) & 4294967295;
19088
- h1 ^= h1 >>> 13;
19089
- h1 = (h1 & 65535) * 3266489909 + (((h1 >>> 16) * 3266489909 & 65535) << 16) & 4294967295;
19090
- h1 ^= h1 >>> 16;
19091
- return h1 >>> 0;
19092
- }
19093
19300
  function resolveQueryChange2(currentResult, event3, options) {
19094
19301
  const index = currentResult.findIndex((e2) => e2._id === event3.id);
19095
19302
  const isInCurrentResult = index !== -1;
@@ -19102,12 +19309,12 @@ function resolveQueryChange2(currentResult, event3, options) {
19102
19309
  const shouldBeInResult = checkItemMatchesWhere2(event3.item, options.where);
19103
19310
  if (isInCurrentResult && shouldBeInResult) {
19104
19311
  const newResult = currentResult.toSpliced(index, 1, event3.item);
19105
- return applyOrderByAndLimit2(newResult, options);
19312
+ return applyOrderByAndLimit(newResult, options);
19106
19313
  } else if (isInCurrentResult && !shouldBeInResult) {
19107
19314
  return currentResult.toSpliced(index, 1);
19108
19315
  } else if (!isInCurrentResult && shouldBeInResult) {
19109
19316
  const newResult = [...currentResult, event3.item];
19110
- return applyOrderByAndLimit2(newResult, options);
19317
+ return applyOrderByAndLimit(newResult, options);
19111
19318
  }
19112
19319
  return false;
19113
19320
  }
@@ -19146,7 +19353,7 @@ function checkItemMatchesWhere2(item, where) {
19146
19353
  });
19147
19354
  });
19148
19355
  }
19149
- function applyOrderByAndLimit2(result, options) {
19356
+ function applyOrderByAndLimit(result, options) {
19150
19357
  let sorted = result;
19151
19358
  if (options.orderBy) {
19152
19359
  sorted = [...result].sort((a2, b4) => {
@@ -19502,7 +19709,7 @@ class Model3 {
19502
19709
  return s;
19503
19710
  }
19504
19711
  }
19505
- function applyQueryChanges2(result, changes) {
19712
+ function applyQueryChanges3(result, changes) {
19506
19713
  const next = [...result];
19507
19714
  for (const change of changes) {
19508
19715
  if (change.type === "delete") {
@@ -19555,7 +19762,7 @@ class StreamingQueryCache2 {
19555
19762
  if (!newEntry.hasResult || !Array.isArray(newEntry.result)) {
19556
19763
  return;
19557
19764
  }
19558
- newEntry.result = applyQueryChanges2(newEntry.result, changes);
19765
+ newEntry.result = applyQueryChanges3(newEntry.result, changes);
19559
19766
  this.notify(newEntry);
19560
19767
  }
19561
19768
  });
@@ -20514,7 +20721,7 @@ var Operation2, PROXY_DRAFT2, RAW_RETURN_SYMBOL2, iteratorSymbol2, dataTypes2, i
20514
20721
  }
20515
20722
  return returnValue(result);
20516
20723
  };
20517
- }, create2, constructorString2, TOKEN_PREFIX2 = "arc:token:", DEFAULT_TIMEOUT_MS2 = 8000, provider2 = null, latestSync2 = null, syncSeq2 = 0, eventWireInstanceCounter2 = 0, EVENT_TABLES2, arrayValidator2, ArcArray2, objectValidator2, ArcObject2, ScopedDataStorage2, ArcPrimitive2, stringValidator2, ArcString2, ArcContextElement2, ArcBoolean2, ArcId2, numberValidator2, ArcNumber2, ArcEvent2, ArcCommand2, ArcListener2, ArcRoute2, ForkedStoreState2, ForkedDataStorage2, MasterStoreState2, MasterDataStorage3, dateValidator2, SQLiteReadWriteTransaction, createSQLiteAdapterFactory = (db) => {
20724
+ }, create2, constructorString2, TOKEN_PREFIX2 = "arc:token:", DEFAULT_TIMEOUT_MS2 = 8000, provider2 = null, latestSync2 = null, syncSeq2 = 0, eventWireInstanceCounter2 = 0, EVENT_TABLES2, arrayValidator2, ArcArray2, objectValidator2, ArcObject2, ScopedDataStorage2, ArcAuthenticationError2, ArcAuthorizationError3, ArcValidationError3, ArcPrimitive2, stringValidator2, ArcString2, ArcContextElement2, ArcBoolean2, ArcId2, numberValidator2, ArcNumber2, ArcEvent2, ArcCommand2, ArcListener2, ArcRoute2, ForkedStoreState2, ForkedDataStorage2, MasterStoreState2, MasterDataStorage3, dateValidator2, _te2, _td2, SQLiteReadWriteTransaction, createSQLiteAdapterFactory = (db) => {
20518
20725
  return async (context) => {
20519
20726
  const adapter = new SQLiteAdapter(db, context);
20520
20727
  await adapter.initialize();
@@ -21222,6 +21429,29 @@ var init_dist2 = __esm(() => {
21222
21429
  return this.#inner.getReadWriteTransaction();
21223
21430
  }
21224
21431
  };
21432
+ ArcAuthenticationError2 = class ArcAuthenticationError2 extends Error {
21433
+ arcCode = "AUTHENTICATION";
21434
+ constructor(message = "Authentication required") {
21435
+ super(message);
21436
+ this.name = "ArcAuthenticationError";
21437
+ }
21438
+ };
21439
+ ArcAuthorizationError3 = class ArcAuthorizationError3 extends Error {
21440
+ arcCode = "AUTHORIZATION";
21441
+ constructor(message = "Forbidden") {
21442
+ super(message);
21443
+ this.name = "ArcAuthorizationError";
21444
+ }
21445
+ };
21446
+ ArcValidationError3 = class ArcValidationError3 extends Error {
21447
+ arcCode = "VALIDATION";
21448
+ fields;
21449
+ constructor(errors, message = "Invalid parameters") {
21450
+ super(message);
21451
+ this.name = "ArcValidationError";
21452
+ this.fields = errors && typeof errors === "object" ? Object.keys(errors) : [];
21453
+ }
21454
+ };
21225
21455
  ArcPrimitive2 = class ArcPrimitive2 extends ArcAbstract2 {
21226
21456
  serialize(value) {
21227
21457
  return value;
@@ -21383,6 +21613,8 @@ var init_dist2 = __esm(() => {
21383
21613
  ArcContextElement2 = class ArcContextElement2 extends ArcFragmentBase2 {
21384
21614
  name;
21385
21615
  types = ["context-element"];
21616
+ __contextId;
21617
+ __contextName;
21386
21618
  get id() {
21387
21619
  return this.name;
21388
21620
  }
@@ -21690,6 +21922,8 @@ var init_dist2 = __esm(() => {
21690
21922
  if (!this.data.handler) {
21691
21923
  throw new Error(`Command "${this.data.name}" has no handler`);
21692
21924
  }
21925
+ this.#fn.validateParams(params);
21926
+ await this.#fn.authorize(adapters);
21693
21927
  const context2 = this.buildCommandContext(adapters);
21694
21928
  return await this.data.handler(context2, params);
21695
21929
  }
@@ -21800,9 +22034,10 @@ var init_dist2 = __esm(() => {
21800
22034
  }
21801
22035
  }
21802
22036
  if (this.data.isAsync) {
21803
- Promise.resolve(this.data.handler(context2, event2)).catch((error) => {
22037
+ const work = Promise.resolve(this.data.handler(context2, event2)).catch((error) => {
21804
22038
  console.error(`Async listener "${this.data.name}" error:`, error);
21805
22039
  });
22040
+ adapters.eventPublisher?.track?.(work);
21806
22041
  } else {
21807
22042
  await this.data.handler(context2, event2);
21808
22043
  }
@@ -22256,6 +22491,8 @@ var init_dist2 = __esm(() => {
22256
22491
  }
22257
22492
  };
22258
22493
  dateValidator2 = typeValidatorBuilder2("Date", (value) => value instanceof Date);
22494
+ _te2 = new TextEncoder;
22495
+ _td2 = new TextDecoder;
22259
22496
  SQLiteReadWriteTransaction = class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
22260
22497
  adapter;
22261
22498
  queries = [];
@@ -36687,7 +36924,8 @@ class ContextHandler {
36687
36924
  type: event.type,
36688
36925
  payload: event.payload,
36689
36926
  createdAt: new Date(event.createdAt),
36690
- authContext: eventAuthContext
36927
+ authContext: eventAuthContext,
36928
+ definition: eventDef
36691
36929
  });
36692
36930
  persistedEvents.push(syncableEvent);
36693
36931
  }
@@ -37464,14 +37702,34 @@ function arcHttpHandlers(ch, cm) {
37464
37702
  ];
37465
37703
  }
37466
37704
  // ../host/src/middleware/ws.ts
37467
- import { LiveQuery } from "@arcote.tech/arc";
37468
- var clientQuerySubs = new Map;
37469
- function cleanupClientSubs(clientId) {
37470
- const subs = clientQuerySubs.get(clientId);
37705
+ import { applyQueryChanges, LiveQuery } from "@arcote.tech/arc";
37706
+ var sharedQueries = new Map;
37707
+ var clientSubKeys = new Map;
37708
+ function sharedKeyFor(descriptor, scope, rawToken) {
37709
+ return JSON.stringify([scope, rawToken, descriptor]);
37710
+ }
37711
+ function detachSub(clientId, subscriptionId, sharedKey) {
37712
+ const entry = sharedQueries.get(sharedKey);
37713
+ if (!entry)
37714
+ return;
37715
+ const subs = entry.subscribers.get(clientId);
37471
37716
  if (subs) {
37472
- for (const live of subs.values())
37473
- live.stop();
37474
- clientQuerySubs.delete(clientId);
37717
+ subs.delete(subscriptionId);
37718
+ if (subs.size === 0)
37719
+ entry.subscribers.delete(clientId);
37720
+ }
37721
+ if (entry.subscribers.size === 0) {
37722
+ entry.live.stop();
37723
+ sharedQueries.delete(sharedKey);
37724
+ }
37725
+ }
37726
+ function cleanupClientSubs(clientId) {
37727
+ const keys = clientSubKeys.get(clientId);
37728
+ if (keys) {
37729
+ for (const [subscriptionId, sharedKey] of keys) {
37730
+ detachSub(clientId, subscriptionId, sharedKey);
37731
+ }
37732
+ clientSubKeys.delete(clientId);
37475
37733
  }
37476
37734
  }
37477
37735
  function scopeAuthHandler() {
@@ -37580,37 +37838,90 @@ function querySubscriptionHandler() {
37580
37838
  if (!rawToken && client.scopeTokens.size > 0) {
37581
37839
  rawToken = client.scopeTokens.values().next().value.raw;
37582
37840
  }
37583
- clientQuerySubs.get(client.id)?.get(subscriptionId)?.stop();
37584
- const live = new LiveQuery(ctx.contextHandler.getModel(), descriptor, scope, rawToken, (update) => {
37585
- if (update.type === "changes") {
37586
- ctx.connectionManager.sendToClient(client.id, {
37587
- type: "query-changes",
37588
- subscriptionId,
37589
- changes: update.changes
37590
- });
37591
- } else {
37592
- ctx.connectionManager.sendToClient(client.id, {
37593
- type: "query-snapshot",
37594
- subscriptionId,
37595
- result: update.result ?? null
37596
- });
37841
+ const prevKey = clientSubKeys.get(client.id)?.get(subscriptionId);
37842
+ if (prevKey)
37843
+ detachSub(client.id, subscriptionId, prevKey);
37844
+ const sharedKey = sharedKeyFor(descriptor, scope, rawToken);
37845
+ const attach = (entry2) => {
37846
+ if (!entry2.subscribers.has(client.id)) {
37847
+ entry2.subscribers.set(client.id, new Set);
37597
37848
  }
37598
- });
37599
- if (!clientQuerySubs.has(client.id)) {
37600
- clientQuerySubs.set(client.id, new Map);
37601
- }
37602
- clientQuerySubs.get(client.id).set(subscriptionId, live);
37603
- try {
37604
- const result = await live.start();
37849
+ entry2.subscribers.get(client.id).add(subscriptionId);
37850
+ if (!clientSubKeys.has(client.id)) {
37851
+ clientSubKeys.set(client.id, new Map);
37852
+ }
37853
+ clientSubKeys.get(client.id).set(subscriptionId, sharedKey);
37854
+ };
37855
+ const existing = sharedQueries.get(sharedKey);
37856
+ if (existing && existing.ready) {
37857
+ attach(existing);
37605
37858
  ctx.connectionManager.sendToClient(client.id, {
37606
37859
  type: "query-snapshot",
37607
37860
  subscriptionId,
37608
- result: result ?? null
37861
+ result: existing.lastResult ?? null
37609
37862
  });
37610
- live.flush();
37863
+ return true;
37864
+ }
37865
+ if (existing) {
37866
+ attach(existing);
37867
+ return true;
37868
+ }
37869
+ const entry = {
37870
+ live: null,
37871
+ subscribers: new Map,
37872
+ lastResult: null,
37873
+ ready: false
37874
+ };
37875
+ entry.live = new LiveQuery(ctx.contextHandler.getModel(), descriptor, scope, rawToken, (update) => {
37876
+ if (update.type === "changes") {
37877
+ if (Array.isArray(entry.lastResult)) {
37878
+ entry.lastResult = applyQueryChanges(entry.lastResult, update.changes);
37879
+ }
37880
+ } else {
37881
+ entry.lastResult = update.result ?? null;
37882
+ }
37883
+ for (const [subClientId, subIds] of entry.subscribers) {
37884
+ for (const subId of subIds) {
37885
+ ctx.connectionManager.sendToClient(subClientId, update.type === "changes" ? {
37886
+ type: "query-changes",
37887
+ subscriptionId: subId,
37888
+ changes: update.changes
37889
+ } : {
37890
+ type: "query-snapshot",
37891
+ subscriptionId: subId,
37892
+ result: update.result ?? null
37893
+ });
37894
+ }
37895
+ }
37896
+ });
37897
+ sharedQueries.set(sharedKey, entry);
37898
+ attach(entry);
37899
+ try {
37900
+ const result = await entry.live.start();
37901
+ entry.lastResult = result ?? null;
37902
+ entry.ready = true;
37903
+ for (const [subClientId, subIds] of entry.subscribers) {
37904
+ for (const subId of subIds) {
37905
+ ctx.connectionManager.sendToClient(subClientId, {
37906
+ type: "query-snapshot",
37907
+ subscriptionId: subId,
37908
+ result: result ?? null
37909
+ });
37910
+ }
37911
+ }
37912
+ entry.live.flush();
37611
37913
  } catch (err3) {
37612
- live.stop();
37613
- clientQuerySubs.get(client.id)?.delete(subscriptionId);
37914
+ entry.live.stop();
37915
+ sharedQueries.delete(sharedKey);
37916
+ for (const subClientId of entry.subscribers.keys()) {
37917
+ const keys = clientSubKeys.get(subClientId);
37918
+ if (keys) {
37919
+ for (const [subId, key] of keys) {
37920
+ if (key === sharedKey)
37921
+ keys.delete(subId);
37922
+ }
37923
+ }
37924
+ }
37614
37925
  console.error(`[Arc] Query subscription error (${descriptor?.element}.${descriptor?.method}):`, err3);
37615
37926
  ctx.connectionManager.sendToClient(client.id, {
37616
37927
  type: "error",
@@ -37620,11 +37931,10 @@ function querySubscriptionHandler() {
37620
37931
  return true;
37621
37932
  }
37622
37933
  if (message.type === "unsubscribe-query") {
37623
- const subs = clientQuerySubs.get(client.id);
37624
- const live = subs?.get(message.subscriptionId);
37625
- if (live) {
37626
- live.stop();
37627
- subs.delete(message.subscriptionId);
37934
+ const key = clientSubKeys.get(client.id)?.get(message.subscriptionId);
37935
+ if (key) {
37936
+ detachSub(client.id, message.subscriptionId, key);
37937
+ clientSubKeys.get(client.id)?.delete(message.subscriptionId);
37628
37938
  }
37629
37939
  return true;
37630
37940
  }
@@ -37826,7 +38136,7 @@ async function createArcServer(config) {
37826
38136
  cronScheduler,
37827
38137
  stop: () => {
37828
38138
  cronScheduler.stop();
37829
- server.stop();
38139
+ server.stop(true);
37830
38140
  }
37831
38141
  };
37832
38142
  }
@@ -37865,10 +38175,10 @@ function generateShellHtml(appName, manifest, initial, stylesHash) {
37865
38175
  <html lang="en">
37866
38176
  <head>
37867
38177
  <meta charset="UTF-8" />
37868
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
38178
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
37869
38179
  <title>${manifest?.title ?? appName}</title>${manifest?.favicon ? `
37870
38180
  <link rel="icon" href="${manifest.favicon}">` : ""}${manifest ? `
37871
- <link rel="manifest" href="/manifest.json">` : ""}
38181
+ <link rel="manifest" href="/manifest.json${stylesQs}">` : ""}
37872
38182
  <link rel="stylesheet" href="/styles.css${stylesQs}" />
37873
38183
  <link rel="stylesheet" href="/theme.css${stylesQs}" />
37874
38184
  <link rel="modulepreload" href="${initialUrl}" />${otelTag}
@@ -38046,7 +38356,10 @@ function staticFilesHandler(ws, devMode, getManifest) {
38046
38356
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
38047
38357
  });
38048
38358
  if ((path4 === "/manifest.json" || path4 === "/manifest.webmanifest") && ws.manifest) {
38049
- return serveFile(ws.manifest.path, ctx.corsHeaders);
38359
+ return serveFile(ws.manifest.path, {
38360
+ ...ctx.corsHeaders,
38361
+ "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable"
38362
+ });
38050
38363
  }
38051
38364
  if (path4.lastIndexOf(".") > path4.lastIndexOf("/")) {
38052
38365
  const publicFile = join21(ws.publicDir, path4.slice(1));
@@ -38064,7 +38377,12 @@ function apiEndpointsHandler(ws, getManifest, cm, moduleAccessMap, telemetry) {
38064
38377
  if (payloads.length === 0 && ctx.tokenPayload) {
38065
38378
  payloads = [ctx.tokenPayload];
38066
38379
  }
38067
- return filterManifestForTokens(getManifest(), moduleAccessMap, payloads).then((filtered) => Response.json(filtered, { headers: ctx.corsHeaders }));
38380
+ return filterManifestForTokens(getManifest(), moduleAccessMap, payloads).then((filtered) => Response.json(filtered, {
38381
+ headers: {
38382
+ ...ctx.corsHeaders,
38383
+ "Cache-Control": "no-cache, private"
38384
+ }
38385
+ }));
38068
38386
  }
38069
38387
  if (url.pathname === "/api/translations") {
38070
38388
  const config = readTranslationsConfig(ws.rootDir);
@@ -38114,7 +38432,11 @@ function devReloadHandler(sseClients) {
38114
38432
  function spaFallbackHandler(getShellHtml) {
38115
38433
  return (_req, _url, ctx) => {
38116
38434
  return new Response(getShellHtml(), {
38117
- headers: { ...ctx.corsHeaders, "Content-Type": "text/html" }
38435
+ headers: {
38436
+ ...ctx.corsHeaders,
38437
+ "Content-Type": "text/html",
38438
+ "Cache-Control": "no-cache, must-revalidate"
38439
+ }
38118
38440
  });
38119
38441
  };
38120
38442
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-cli",
3
- "version": "0.7.27",
3
+ "version": "0.7.28",
4
4
  "description": "CLI tool for Arc framework",
5
5
  "module": "index.ts",
6
6
  "main": "dist/index.js",
@@ -12,13 +12,13 @@
12
12
  "build": "bun build --target=bun ./src/index.ts --outdir=dist --external @arcote.tech/arc --external @arcote.tech/arc-ds --external @arcote.tech/arc-react --external @arcote.tech/platform --external @arcote.tech/arc-map --external '@opentelemetry/*' && chmod +x dist/index.js"
13
13
  },
14
14
  "dependencies": {
15
- "@arcote.tech/arc": "^0.7.27",
16
- "@arcote.tech/arc-ds": "^0.7.27",
17
- "@arcote.tech/arc-react": "^0.7.27",
18
- "@arcote.tech/arc-host": "^0.7.27",
19
- "@arcote.tech/arc-adapter-db-sqlite": "^0.7.27",
20
- "@arcote.tech/arc-adapter-db-postgres": "^0.7.27",
21
- "@arcote.tech/arc-otel": "^0.7.27",
15
+ "@arcote.tech/arc": "^0.7.28",
16
+ "@arcote.tech/arc-ds": "^0.7.28",
17
+ "@arcote.tech/arc-react": "^0.7.28",
18
+ "@arcote.tech/arc-host": "^0.7.28",
19
+ "@arcote.tech/arc-adapter-db-sqlite": "^0.7.28",
20
+ "@arcote.tech/arc-adapter-db-postgres": "^0.7.28",
21
+ "@arcote.tech/arc-otel": "^0.7.28",
22
22
  "@opentelemetry/api": "^1.9.0",
23
23
  "@opentelemetry/api-logs": "^0.57.0",
24
24
  "@opentelemetry/core": "^1.30.0",
@@ -31,8 +31,8 @@
31
31
  "@opentelemetry/sdk-trace-base": "^1.30.0",
32
32
  "@opentelemetry/sdk-trace-node": "^1.30.0",
33
33
  "@opentelemetry/semantic-conventions": "^1.27.0",
34
- "@arcote.tech/platform": "^0.7.27",
35
- "@arcote.tech/arc-map": "^0.7.27",
34
+ "@arcote.tech/platform": "^0.7.28",
35
+ "@arcote.tech/arc-map": "^0.7.28",
36
36
  "@clack/prompts": "^0.9.0",
37
37
  "commander": "^11.1.0",
38
38
  "chokidar": "^3.5.3",
@@ -134,8 +134,8 @@ export function generateShellHtml(
134
134
  <html lang="en">
135
135
  <head>
136
136
  <meta charset="UTF-8" />
137
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
138
- <title>${manifest?.title ?? appName}</title>${manifest?.favicon ? `\n <link rel="icon" href="${manifest.favicon}">` : ""}${manifest ? `\n <link rel="manifest" href="/manifest.json">` : ""}
137
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
138
+ <title>${manifest?.title ?? appName}</title>${manifest?.favicon ? `\n <link rel="icon" href="${manifest.favicon}">` : ""}${manifest ? `\n <link rel="manifest" href="/manifest.json${stylesQs}">` : ""}
139
139
  <link rel="stylesheet" href="/styles.css${stylesQs}" />
140
140
  <link rel="stylesheet" href="/theme.css${stylesQs}" />
141
141
  <link rel="modulepreload" href="${initialUrl}" />${otelTag}
@@ -403,9 +403,15 @@ function staticFilesHandler(
403
403
  "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable",
404
404
  });
405
405
 
406
- // Serve manifest.json from root dir
406
+ // Serve manifest.json from root dir. Stable URL, content changes between
407
+ // builds — HTML references it with `?v=<stylesHash>` (generateShellHtml),
408
+ // so the URL changes on rebuild and immutable caching is safe. Handler
409
+ // ignores the query string itself (same pattern as /styles.css).
407
410
  if ((path === "/manifest.json" || path === "/manifest.webmanifest") && ws.manifest) {
408
- return serveFile(ws.manifest.path, ctx.corsHeaders);
411
+ return serveFile(ws.manifest.path, {
412
+ ...ctx.corsHeaders,
413
+ "Cache-Control": devMode ? "no-cache" : "max-age=31536000,immutable",
414
+ });
409
415
  }
410
416
 
411
417
  // Public files (files with extensions)
@@ -436,7 +442,18 @@ function apiEndpointsHandler(
436
442
  payloads = [ctx.tokenPayload];
437
443
  }
438
444
  return filterManifestForTokens(getManifest(), moduleAccessMap, payloads)
439
- .then((filtered) => Response.json(filtered, { headers: ctx.corsHeaders }));
445
+ .then((filtered) =>
446
+ Response.json(filtered, {
447
+ headers: {
448
+ ...ctx.corsHeaders,
449
+ // Treść zależy od tokenu (filterManifestForTokens) i zmienia się
450
+ // co deploy. Nie jest artefaktem buildu — `private`, by pośredni
451
+ // cache nie podał manifestu jednego usera drugiemu, `no-cache`,
452
+ // by po deployu klient dostał świeże hashe/podpisy chunków.
453
+ "Cache-Control": "no-cache, private",
454
+ },
455
+ }),
456
+ );
440
457
  }
441
458
 
442
459
  if (url.pathname === "/api/translations") {
@@ -501,7 +518,15 @@ function devReloadHandler(
501
518
  function spaFallbackHandler(getShellHtml: () => string): ArcHttpHandler {
502
519
  return (_req, _url, ctx) => {
503
520
  return new Response(getShellHtml(), {
504
- headers: { ...ctx.corsHeaders, "Content-Type": "text/html" },
521
+ headers: {
522
+ ...ctx.corsHeaders,
523
+ "Content-Type": "text/html",
524
+ // Stabilny URL, zmienna treść (embeduje initial.<hash>.js + styles ?v=).
525
+ // Kotwica całego łańcucha — musi być zawsze rewalidowana, inaczej po
526
+ // deployu przeglądarka/CDN podaje stary shell wskazujący na nieistniejące
527
+ // już bundle. Hash nie pomoże: to URL trasy, którego nie da się zahashować.
528
+ "Cache-Control": "no-cache, must-revalidate",
529
+ },
505
530
  });
506
531
  };
507
532
  }