@arcote.tech/arc-adapter-db-postgres 0.7.27 → 0.7.29

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 +237 -37
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1829,9 +1829,12 @@ class LocalEventPublisher {
1829
1829
  store: EVENT_TABLES.events,
1830
1830
  changes: [{ type: "set", data: storedEvent }]
1831
1831
  });
1832
- if (event.payload && typeof event.payload === "object") {
1832
+ const tagFields = event.definition?.tagFields ?? [];
1833
+ if (tagFields.length > 0 && event.payload && typeof event.payload === "object") {
1834
+ const payload = event.payload;
1833
1835
  const tagChanges = [];
1834
- for (const [key, value] of Object.entries(event.payload)) {
1836
+ for (const key of tagFields) {
1837
+ const value = payload[key];
1835
1838
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
1836
1839
  tagChanges.push({
1837
1840
  type: "set",
@@ -2055,6 +2058,11 @@ class ArcOptional {
2055
2058
  return false;
2056
2059
  return this.parent.validate(value);
2057
2060
  }
2061
+ coerce(value) {
2062
+ if (value == null)
2063
+ return;
2064
+ return this.parent.coerce(value);
2065
+ }
2058
2066
  toJsonSchema() {
2059
2067
  const parentSchema = this.parent.toJsonSchema?.() ?? {};
2060
2068
  return {
@@ -2101,6 +2109,9 @@ class ArcBranded {
2101
2109
  validate(value) {
2102
2110
  return this.parent.validate(value);
2103
2111
  }
2112
+ coerce(value) {
2113
+ return this.parent.coerce(value);
2114
+ }
2104
2115
  toJsonSchema() {
2105
2116
  return this.parent.toJsonSchema?.() ?? {};
2106
2117
  }
@@ -2125,6 +2136,11 @@ class ArcDefault {
2125
2136
  return false;
2126
2137
  return this.parent.validate(value);
2127
2138
  }
2139
+ coerce(value) {
2140
+ if (value === undefined || value === null)
2141
+ return;
2142
+ return this.parent.coerce(value);
2143
+ }
2128
2144
  parse(value) {
2129
2145
  if (value)
2130
2146
  return this.parent.parse(value);
@@ -2207,6 +2223,9 @@ class ArcAbstract {
2207
2223
  }
2208
2224
  return false;
2209
2225
  }
2226
+ coerce(value) {
2227
+ return this.validate(value) ? undefined : value;
2228
+ }
2210
2229
  pipeValidation(name, validator) {
2211
2230
  const newInstance = this.clone();
2212
2231
  newInstance.validations = [...this.validations, { name, validator }];
@@ -2344,6 +2363,17 @@ class ArcArray extends ArcAbstract {
2344
2363
  return { msg: "array is empty" };
2345
2364
  });
2346
2365
  }
2366
+ coerce(value) {
2367
+ if (!Array.isArray(value))
2368
+ return;
2369
+ const out = [];
2370
+ for (const item of value) {
2371
+ const coerced = this.parent.coerce(item);
2372
+ if (coerced !== undefined)
2373
+ out.push(coerced);
2374
+ }
2375
+ return this.validate(out) ? undefined : out;
2376
+ }
2347
2377
  parse(value) {
2348
2378
  return value.map((v) => this.parent.parse(v));
2349
2379
  }
@@ -2415,6 +2445,19 @@ class ArcObject extends ArcAbstract {
2415
2445
  ]);
2416
2446
  this.rawShape = rawShape;
2417
2447
  }
2448
+ coerce(value) {
2449
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
2450
+ return;
2451
+ }
2452
+ const src = value;
2453
+ const out = {};
2454
+ for (const [key, element] of Object.entries(this.rawShape)) {
2455
+ const coerced = element.coerce(src[key]);
2456
+ if (coerced !== undefined)
2457
+ out[key] = coerced;
2458
+ }
2459
+ return this.validate(out) ? undefined : out;
2460
+ }
2418
2461
  parse(value) {
2419
2462
  return Object.entries(this.rawShape).reduce((acc, [key, element]) => {
2420
2463
  acc[key] = element.parse(value[key]);
@@ -2664,6 +2707,25 @@ class ArcAuthorizationError extends Error {
2664
2707
  this.name = "ArcAuthorizationError";
2665
2708
  }
2666
2709
  }
2710
+ function collectFieldPaths(errors, prefix = "") {
2711
+ if (!errors || typeof errors !== "object")
2712
+ return [];
2713
+ const schema = errors.schema;
2714
+ if (!schema || typeof schema !== "object")
2715
+ return [];
2716
+ const out = [];
2717
+ for (const [field, fieldError] of Object.entries(schema)) {
2718
+ if (!fieldError)
2719
+ continue;
2720
+ const path = prefix ? `${prefix}.${field}` : field;
2721
+ const nested = collectFieldPaths(fieldError, path);
2722
+ if (nested.length > 0)
2723
+ out.push(...nested);
2724
+ else
2725
+ out.push(path);
2726
+ }
2727
+ return out;
2728
+ }
2667
2729
 
2668
2730
  class ArcValidationError extends Error {
2669
2731
  arcCode = "VALIDATION";
@@ -2671,7 +2733,8 @@ class ArcValidationError extends Error {
2671
2733
  constructor(errors, message = "Invalid parameters") {
2672
2734
  super(message);
2673
2735
  this.name = "ArcValidationError";
2674
- this.fields = errors && typeof errors === "object" ? Object.keys(errors) : [];
2736
+ const paths = collectFieldPaths(errors);
2737
+ this.fields = paths.length > 0 ? paths : errors && typeof errors === "object" ? Object.keys(errors) : [];
2675
2738
  }
2676
2739
  }
2677
2740
  class ArcPrimitive extends ArcAbstract {
@@ -2848,12 +2911,35 @@ class ArcContextElement extends ArcFragmentBase {
2848
2911
  types = ["context-element"];
2849
2912
  __contextId;
2850
2913
  __contextName;
2914
+ __contextPath;
2851
2915
  get id() {
2852
2916
  return this.name;
2853
2917
  }
2918
+ __declStack;
2854
2919
  constructor(name) {
2855
2920
  super();
2856
2921
  this.name = name;
2922
+ this.__declStack = new Error().stack;
2923
+ }
2924
+ getSourceLocation() {
2925
+ const stack = this.__declStack;
2926
+ if (!stack)
2927
+ return;
2928
+ for (const raw of stack.split(`
2929
+ `).slice(1)) {
2930
+ const m = raw.match(/\(?((?:file:\/\/)?[^()\s]+?):(\d+):(\d+)\)?$/);
2931
+ if (!m)
2932
+ continue;
2933
+ let file = m[1];
2934
+ if (file.startsWith("file://"))
2935
+ file = file.slice(7);
2936
+ const isCoreInternal = /packages\/core\/(src|dist)\//.test(file) && !/\.test\.tsx?$/.test(file);
2937
+ if (file.includes("node_modules") || isCoreInternal || file.includes("native:") || !file.includes("/")) {
2938
+ continue;
2939
+ }
2940
+ return { file, line: Number(m[2]) };
2941
+ }
2942
+ return;
2857
2943
  }
2858
2944
  _seeds;
2859
2945
  getSeeds() {
@@ -3038,7 +3124,8 @@ class ArcEvent extends ArcContextElement {
3038
3124
  payload,
3039
3125
  id: this.eventId.generate(),
3040
3126
  createdAt: new Date,
3041
- authContext
3127
+ authContext,
3128
+ definition: this
3042
3129
  };
3043
3130
  await adapters.eventPublisher.publish(event);
3044
3131
  }
@@ -3091,8 +3178,8 @@ class ArcEvent extends ArcContextElement {
3091
3178
  const tagsSchema = new ArcObject({
3092
3179
  _id: new ArcString().primaryKey(),
3093
3180
  eventId: new ArcString().index(),
3094
- tagKey: new ArcString().index(),
3095
- tagValue: new ArcString().index()
3181
+ tagKey: new ArcString,
3182
+ tagValue: new ArcString
3096
3183
  });
3097
3184
  const syncStatusSchema = new ArcObject({
3098
3185
  _id: new ArcString().primaryKey(),
@@ -3234,6 +3321,34 @@ class ArcFunction {
3234
3321
  };
3235
3322
  }
3236
3323
  }
3324
+ function serializeParams(schema, params) {
3325
+ if (schema && typeof schema.serialize === "function") {
3326
+ return schema.serialize(params ?? {});
3327
+ }
3328
+ return params;
3329
+ }
3330
+ function deserializeParams(schema, params) {
3331
+ if (schema && typeof schema.deserialize === "function") {
3332
+ return schema.deserialize(params ?? {});
3333
+ }
3334
+ return params;
3335
+ }
3336
+ function deserializeResult(schemas, value) {
3337
+ const list = Array.isArray(schemas) ? schemas : undefined;
3338
+ if (!list || list.length !== 1)
3339
+ return value;
3340
+ const schema = list[0];
3341
+ if (!schema || typeof schema.deserialize !== "function")
3342
+ return value;
3343
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
3344
+ return value;
3345
+ }
3346
+ try {
3347
+ return schema.deserialize(value);
3348
+ } catch {
3349
+ return value;
3350
+ }
3351
+ }
3237
3352
  class AggregateBase {
3238
3353
  value;
3239
3354
  _id;
@@ -3360,7 +3475,8 @@ class ArcCommand extends ArcContextElement {
3360
3475
  scope: adapters.scope.scopeName,
3361
3476
  token: adapters.scope.getToken()
3362
3477
  } : undefined;
3363
- return await adapters.commandWire.executeCommand(this.data.name, params, wireAuth);
3478
+ const wireResult = await adapters.commandWire.executeCommand(this.data.name, serializeParams(this.data.params, params), wireAuth);
3479
+ return deserializeResult(this.data.results, wireResult);
3364
3480
  };
3365
3481
  return Object.assign(executeFunc, { params: this.data.params });
3366
3482
  }
@@ -3368,10 +3484,11 @@ class ArcCommand extends ArcContextElement {
3368
3484
  if (!this.data.handler) {
3369
3485
  throw new Error(`Command "${this.data.name}" has no handler`);
3370
3486
  }
3371
- this.#fn.validateParams(params);
3487
+ const input = deserializeParams(this.data.params, params);
3488
+ this.#fn.validateParams(input);
3372
3489
  await this.#fn.authorize(adapters);
3373
3490
  const context2 = this.buildCommandContext(adapters);
3374
- return await this.data.handler(context2, params);
3491
+ return await this.data.handler(context2, input);
3375
3492
  }
3376
3493
  buildCommandContext(adapters) {
3377
3494
  const context2 = this.#fn.buildContext(adapters);
@@ -4033,16 +4150,57 @@ function resolveQueryChange(currentResult, event3, options) {
4033
4150
  }
4034
4151
  const shouldBeInResult = checkItemMatchesWhere(event3.item, options.where);
4035
4152
  if (isInCurrentResult && shouldBeInResult) {
4036
- const newResult = currentResult.toSpliced(index, 1, event3.item);
4037
- return applyOrderByAndLimit(newResult, options);
4153
+ if (!options.orderBy) {
4154
+ return applyLimit(currentResult.toSpliced(index, 1, event3.item), options);
4155
+ }
4156
+ const cmp = compareByOrderBy(options.orderBy);
4157
+ if (cmp(currentResult[index], event3.item) === 0) {
4158
+ return applyLimit(currentResult.toSpliced(index, 1, event3.item), options);
4159
+ }
4160
+ const without = currentResult.toSpliced(index, 1);
4161
+ const at = upperBound(without, event3.item, cmp);
4162
+ return applyLimit(without.toSpliced(at, 0, event3.item), options);
4038
4163
  } else if (isInCurrentResult && !shouldBeInResult) {
4039
4164
  return currentResult.toSpliced(index, 1);
4040
4165
  } else if (!isInCurrentResult && shouldBeInResult) {
4041
- const newResult = [...currentResult, event3.item];
4042
- return applyOrderByAndLimit(newResult, options);
4166
+ if (!options.orderBy) {
4167
+ return applyLimit([...currentResult, event3.item], options);
4168
+ }
4169
+ const cmp = compareByOrderBy(options.orderBy);
4170
+ const at = upperBound(currentResult, event3.item, cmp);
4171
+ return applyLimit(currentResult.toSpliced(at, 0, event3.item), options);
4043
4172
  }
4044
4173
  return false;
4045
4174
  }
4175
+ function compareByOrderBy(orderBy) {
4176
+ const entries = Object.entries(orderBy);
4177
+ return (a, b) => {
4178
+ for (const [key, direction] of entries) {
4179
+ const aVal = a[key];
4180
+ const bVal = b[key];
4181
+ if (aVal < bVal)
4182
+ return direction === "asc" ? -1 : 1;
4183
+ if (aVal > bVal)
4184
+ return direction === "asc" ? 1 : -1;
4185
+ }
4186
+ return 0;
4187
+ };
4188
+ }
4189
+ function upperBound(arr, item, cmp) {
4190
+ let lo = 0;
4191
+ let hi = arr.length;
4192
+ while (lo < hi) {
4193
+ const mid = lo + hi >> 1;
4194
+ if (cmp(arr[mid], item) <= 0)
4195
+ lo = mid + 1;
4196
+ else
4197
+ hi = mid;
4198
+ }
4199
+ return lo;
4200
+ }
4201
+ function applyLimit(result, options) {
4202
+ return options.limit !== undefined && result.length > options.limit ? result.slice(0, options.limit) : result;
4203
+ }
4046
4204
  function checkItemMatchesWhere(item, where) {
4047
4205
  if (!where) {
4048
4206
  return true;
@@ -4078,27 +4236,6 @@ function checkItemMatchesWhere(item, where) {
4078
4236
  });
4079
4237
  });
4080
4238
  }
4081
- function applyOrderByAndLimit(result, options) {
4082
- let sorted = result;
4083
- if (options.orderBy) {
4084
- sorted = [...result].sort((a, b) => {
4085
- for (const [key, direction] of Object.entries(options.orderBy)) {
4086
- const aVal = a[key];
4087
- const bVal = b[key];
4088
- if (aVal < bVal)
4089
- return direction === "asc" ? -1 : 1;
4090
- if (aVal > bVal)
4091
- return direction === "asc" ? 1 : -1;
4092
- }
4093
- return 0;
4094
- });
4095
- }
4096
- if (options.limit !== undefined) {
4097
- sorted = sorted.slice(0, options.limit);
4098
- }
4099
- return sorted;
4100
- }
4101
-
4102
4239
  class ObservableDataStorage {
4103
4240
  source;
4104
4241
  onChange;
@@ -4254,7 +4391,7 @@ function extractDatabaseAgnosticSchema(arcObject, tableName) {
4254
4391
  columns
4255
4392
  };
4256
4393
  }
4257
- var dateValidator = typeValidatorBuilder("Date", (value) => value instanceof Date);
4394
+ var dateValidator = typeValidatorBuilder("Date", (value) => value instanceof Date && !isNaN(value.getTime()));
4258
4395
  function buildContextAccessor(context2, adapters, contextMethod, onCall) {
4259
4396
  const result = {};
4260
4397
  for (const element2 of context2.elements) {
@@ -6817,6 +6954,40 @@ function osUsername() {
6817
6954
  }
6818
6955
 
6819
6956
  // src/postgres-adapter.ts
6957
+ var MAX_SAFE_INT = BigInt(Number.MAX_SAFE_INTEGER);
6958
+ var MIN_SAFE_INT = BigInt(Number.MIN_SAFE_INTEGER);
6959
+ function unsafeIntMessage(raw, columnName) {
6960
+ const where = columnName ? ` w kolumnie "${columnName}"` : "";
6961
+ return `[PostgreSQL] Wartość całkowita ${raw}${where} przekracza bezpieczny zakres ` + `JS Number (±2^53-1). Odczyt jako number cicho straciłby precyzję. ` + `Użyj kolumny TEXT/NUMERIC i obsłuż wartość jako string/BigInt.`;
6962
+ }
6963
+ function coercePgNumber(value, columnName) {
6964
+ if (value === null || value === undefined)
6965
+ return value;
6966
+ if (typeof value === "number")
6967
+ return value;
6968
+ if (typeof value === "bigint") {
6969
+ if (value > MAX_SAFE_INT || value < MIN_SAFE_INT) {
6970
+ throw new Error(unsafeIntMessage(value.toString(), columnName));
6971
+ }
6972
+ return Number(value);
6973
+ }
6974
+ if (typeof value === "string") {
6975
+ const s = value.trim();
6976
+ if (s === "")
6977
+ return value;
6978
+ if (/^[+-]?\d+$/.test(s)) {
6979
+ const big = BigInt(s);
6980
+ if (big > MAX_SAFE_INT || big < MIN_SAFE_INT) {
6981
+ throw new Error(unsafeIntMessage(s, columnName));
6982
+ }
6983
+ return Number(big);
6984
+ }
6985
+ const n = Number(s);
6986
+ return Number.isNaN(n) ? value : n;
6987
+ }
6988
+ return value;
6989
+ }
6990
+
6820
6991
  class PostgreSQLReadTransaction {
6821
6992
  db;
6822
6993
  tables;
@@ -6838,7 +7009,27 @@ class PostgreSQLReadTransaction {
6838
7009
  deserializeValue(value, column) {
6839
7010
  if (value === null || value === undefined)
6840
7011
  return null;
6841
- switch (column.type.toLowerCase()) {
7012
+ const type = column.type.toLowerCase().split("(")[0].trim();
7013
+ switch (type) {
7014
+ case "bigint":
7015
+ case "int8":
7016
+ case "bigserial":
7017
+ case "serial8":
7018
+ case "serial":
7019
+ case "serial4":
7020
+ case "integer":
7021
+ case "int":
7022
+ case "int4":
7023
+ case "smallint":
7024
+ case "int2":
7025
+ case "numeric":
7026
+ case "decimal":
7027
+ case "real":
7028
+ case "double precision":
7029
+ case "float":
7030
+ case "float4":
7031
+ case "float8":
7032
+ return coercePgNumber(value, column.name);
6842
7033
  case "json":
6843
7034
  case "jsonb":
6844
7035
  if (typeof value === "string") {
@@ -7489,7 +7680,15 @@ class PostgresJsDatabase {
7489
7680
  }
7490
7681
  var createPostgreSQLAdapterFactoryFromUrl = (connectionString) => {
7491
7682
  const sql = src_default(connectionString, {
7492
- onnotice: () => {}
7683
+ onnotice: () => {},
7684
+ types: {
7685
+ bigint: {
7686
+ to: 20,
7687
+ from: [20],
7688
+ serialize: (v) => v.toString(),
7689
+ parse: (v) => coercePgNumber(v)
7690
+ }
7691
+ }
7493
7692
  });
7494
7693
  const db = new PostgresJsDatabase(sql);
7495
7694
  return createPostgreSQLAdapterFactory(db);
@@ -7497,5 +7696,6 @@ var createPostgreSQLAdapterFactoryFromUrl = (connectionString) => {
7497
7696
  export {
7498
7697
  createPostgreSQLAdapterFactoryFromUrl,
7499
7698
  createPostgreSQLAdapterFactory,
7699
+ coercePgNumber,
7500
7700
  PostgreSQLAdapter
7501
7701
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-adapter-db-postgres",
3
- "version": "0.7.27",
3
+ "version": "0.7.29",
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.7.27"
26
+ "@arcote.tech/arc": "^0.7.29"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/pg": "^8.11.0",