@objectstack/types 17.0.0-rc.5 → 17.0.0

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
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  GLOBAL_UNIQUE_CONFIRMATION_REQUIRED: () => GLOBAL_UNIQUE_CONFIRMATION_REQUIRED,
24
24
  GLOBAL_UNIQUE_ISOLATED_PRESCRIPTION: () => GLOBAL_UNIQUE_ISOLATED_PRESCRIPTION,
25
25
  INTERNAL_ERROR_MESSAGE: () => INTERNAL_ERROR_MESSAGE,
26
+ VALIDATION_FAILED_STATUS: () => VALIDATION_FAILED_STATUS,
26
27
  _resetEnvDeprecationWarnings: () => _resetEnvDeprecationWarnings,
27
28
  buildGlobalUniqueStopMessage: () => buildGlobalUniqueStopMessage,
28
29
  collectConfiguredLocales: () => collectConfiguredLocales,
@@ -32,12 +33,17 @@ __export(index_exports, {
32
33
  describeGlobalUniqueFinding: () => describeGlobalUniqueFinding,
33
34
  emitDegradedBootBanner: () => emitDegradedBootBanner,
34
35
  fieldUniqueIsGlobal: () => fieldUniqueIsGlobal,
36
+ fieldsFromZodIssues: () => fieldsFromZodIssues,
35
37
  globalUniqueFindingId: () => globalUniqueFindingId,
36
38
  isMcpServerEnabled: () => isMcpServerEnabled,
37
39
  isModuleNotFoundError: () => isModuleNotFoundError,
38
40
  isPlatformOwnedObject: () => isPlatformOwnedObject,
41
+ isRelationSubObjectPhrase: () => isRelationSubObjectPhrase,
42
+ isUnbackedConflictTargetError: () => isUnbackedConflictTargetError,
43
+ isUniqueViolationError: () => isUniqueViolationError,
39
44
  keysetWalk: () => keysetWalk,
40
45
  looksLikeInternalErrorLeak: () => looksLikeInternalErrorLeak,
46
+ matchMissingColumnOfRelation: () => matchMissingColumnOfRelation,
41
47
  postureGatesGlobalUniques: () => postureGatesGlobalUniques,
42
48
  readEnvWithDeprecation: () => readEnvWithDeprecation,
43
49
  recordGlobalUniqueAttestation: () => recordGlobalUniqueAttestation,
@@ -50,10 +56,14 @@ __export(index_exports, {
50
56
  resolveSandboxTimeoutMs: () => resolveSandboxTimeoutMs,
51
57
  resolveSearchPinyinEnabled: () => resolveSearchPinyinEnabled,
52
58
  resolveTenancyPosture: () => resolveTenancyPosture,
59
+ resolveThrownHttpError: () => resolveThrownHttpError,
53
60
  sendError: () => sendError,
54
61
  sendOk: () => sendOk,
55
62
  stampSearchPinyinEnabled: () => stampSearchPinyinEnabled,
56
- unconfirmedGlobalUniques: () => unconfirmedGlobalUniques
63
+ unconfirmedGlobalUniques: () => unconfirmedGlobalUniques,
64
+ uniqueViolationColumn: () => uniqueViolationColumn,
65
+ validationFailure: () => validationFailure,
66
+ validationFailureDetails: () => validationFailureDetails
57
67
  });
58
68
  module.exports = __toCommonJS(index_exports);
59
69
 
@@ -192,10 +202,25 @@ function _resetEnvDeprecationWarnings() {
192
202
 
193
203
  // src/error-leak.ts
194
204
  var INTERNAL_ERROR_MESSAGE = "Internal server error";
205
+ var DIALECT_LEAK_PHRASINGS = [
206
+ // Postgres 42P01 / 42703 (and, as a superstring, the `… of relation "…"`
207
+ // sub-object family: 42704 and friends). The quotes are required because
208
+ // Postgres always emits them here.
209
+ /\b(?:relation|column)\s+["'`][^"'`]+["'`]\s+does not exist/i,
210
+ // Postgres 42501. Restricted to physical object kinds: `schema`, `view`,
211
+ // `function` and `column` are all ObjectStack AUTHORING vocabulary, so a
212
+ // product message could legitimately use them and a miss is the cheap
213
+ // direction (the outcome is already a 5xx).
214
+ /\bpermission denied for (?:table|relation|sequence|database)\b/i,
215
+ // SQLite/libsql, message-only form. The `sqlite_` limb below catches these
216
+ // only when the driver prefixed its code; `better-sqlite3` and libsql both
217
+ // raise them bare, which is the shape measured across this repo.
218
+ /\bno such (?:table|column):/i
219
+ ];
195
220
  function looksLikeInternalErrorLeak(message) {
196
221
  if (!message) return false;
197
222
  const lower = String(message).toLowerCase();
198
- return lower.includes("sqlite_") || lower.includes("sqlstate") || lower.startsWith("insert into ") || lower.startsWith("update ") || lower.startsWith("select ") || lower.startsWith("delete from ") || lower.includes("constraint failed") || lower.includes("unique constraint") || lower.includes("foreign key");
223
+ return lower.includes("sqlite_") || lower.includes("sqlstate") || lower.startsWith("insert into ") || lower.startsWith("update ") || lower.startsWith("select ") || lower.startsWith("delete from ") || lower.includes("constraint failed") || lower.includes("unique constraint") || lower.includes("foreign key") || DIALECT_LEAK_PHRASINGS.some((pattern) => pattern.test(lower));
199
224
  }
200
225
  function declaresServerFault(err) {
201
226
  if (typeof err !== "object" || err === null) return false;
@@ -280,6 +305,155 @@ function sendError(res, status, code, message, extra) {
280
305
  res.status(status).json({ success: false, error: { code, message, ...extra } });
281
306
  }
282
307
 
308
+ // src/thrown-http-error.ts
309
+ var import_api2 = require("@objectstack/spec/api");
310
+
311
+ // src/validation-failure.ts
312
+ var import_api = require("@objectstack/spec/api");
313
+ var VALIDATION_FAILED_STATUS = 400;
314
+ function validationFailureDetails(err) {
315
+ if (!err) return void 0;
316
+ if (err.code !== "VALIDATION_FAILED" && err.name !== "ValidationError") return void 0;
317
+ return {
318
+ code: "VALIDATION_FAILED",
319
+ fields: Array.isArray(err.fields) ? err.fields : []
320
+ };
321
+ }
322
+ function validationFailure(message, fields) {
323
+ const err = new Error(message);
324
+ err.name = "ValidationError";
325
+ err.code = "VALIDATION_FAILED";
326
+ err.fields = fields;
327
+ return err;
328
+ }
329
+ function fieldsFromZodIssues(issues, ...input) {
330
+ return (0, import_api.zodIssuesToFields)(issues, ...input).map(
331
+ (entry) => entry.field === "" ? { ...entry, field: "(body)" } : entry
332
+ );
333
+ }
334
+
335
+ // src/thrown-http-error.ts
336
+ function resolveThrownHttpError(error, fallbackStatus = 500) {
337
+ const e = error;
338
+ const validation = validationFailureDetails(e);
339
+ const declaredStatus = typeof e?.status === "number" ? e.status : typeof e?.statusCode === "number" ? e.statusCode : validation ? VALIDATION_FAILED_STATUS : void 0;
340
+ const status = declaredStatus ?? fallbackStatus;
341
+ const spelled = typeof e?.code === "string" && e.code !== "" ? e.code : void 0;
342
+ const registered = spelled !== void 0 && import_api2.ErrorCode.safeParse(spelled).success ? spelled : void 0;
343
+ const code = validation ? validation.code : registered ?? (0, import_api2.standardErrorCodeForHttpStatus)(status);
344
+ const declaredCode = validation ? validation.code : spelled;
345
+ const issues = Array.isArray(e?.issues) ? e.issues : void 0;
346
+ const details = {
347
+ // A truthy NON-string `code` (a driver errno, say) is context and stays
348
+ // context — promoting it would put a number in the field callers branch on,
349
+ // which is the drift #3842 removed.
350
+ ...!validation && e?.code && typeof e.code !== "string" ? { code: e.code } : {},
351
+ ...issues ? { issues } : {},
352
+ ...validation ? { fields: validation.fields } : {}
353
+ };
354
+ return {
355
+ status,
356
+ ...declaredStatus !== void 0 ? { declaredStatus } : {},
357
+ code,
358
+ ...declaredCode !== void 0 ? { declaredCode } : {},
359
+ message: typeof e?.message === "string" ? e.message : String(error),
360
+ ...Object.keys(details).length > 0 ? { details } : {}
361
+ };
362
+ }
363
+
364
+ // src/relation-sub-object.ts
365
+ function matchMissingColumnOfRelation(message) {
366
+ return MISSING_COLUMN_OF_RELATION.exec(message)?.[1];
367
+ }
368
+ function isRelationSubObjectPhrase(message) {
369
+ return RELATION_SUB_OBJECT.test(message);
370
+ }
371
+ var MISSING_COLUMN_OF_RELATION = /column\s+["'`]([a-z0-9_]+)["'`]\s+of relation\s+\S+\s+does not exist/i;
372
+ var RELATION_SUB_OBJECT = /["'`][^"'`]+["'`]\s+of relation\s/i;
373
+
374
+ // src/unique-violation.ts
375
+ var UNIQUE_VIOLATION = {
376
+ codes: /* @__PURE__ */ new Set(["23505", "ER_DUP_ENTRY", "SQLITE_CONSTRAINT_UNIQUE"]),
377
+ errnos: /* @__PURE__ */ new Set([1062]),
378
+ message: /unique constraint|unique violation|duplicate key|duplicate entry/i
379
+ };
380
+ var MAX_CAUSE_DEPTH = 4;
381
+ function isUniqueViolationError(error) {
382
+ return matchesUniqueViolation(error, 0);
383
+ }
384
+ function matchesUniqueViolation(error, depth) {
385
+ if (error === null || error === void 0 || depth > MAX_CAUSE_DEPTH) return false;
386
+ if (typeof error === "string") return UNIQUE_VIOLATION.message.test(error);
387
+ if (typeof error !== "object") return false;
388
+ const err = error;
389
+ if (typeof err.code === "string" && UNIQUE_VIOLATION.codes.has(err.code)) return true;
390
+ if (typeof err.code === "number" && UNIQUE_VIOLATION.errnos.has(err.code)) return true;
391
+ if (typeof err.errno === "number" && UNIQUE_VIOLATION.errnos.has(err.errno)) return true;
392
+ if (typeof err.message === "string" && UNIQUE_VIOLATION.message.test(err.message)) return true;
393
+ return matchesUniqueViolation(err.cause, depth + 1);
394
+ }
395
+ var SQLITE_TARGETS = /unique constraint failed:\s*([^\n]*)/i;
396
+ var POSTGRES_DETAIL_TARGETS = /\bkey \(([^)]+)\)=\(/i;
397
+ var SQLITE_INDEX_FORM = /^index\b/i;
398
+ var PLAIN_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_$]*$/;
399
+ function bareIdentifier(raw) {
400
+ const stripped = raw.trim().replace(/[`"'[\]]/g, "");
401
+ const dot = stripped.lastIndexOf(".");
402
+ return dot >= 0 ? stripped.slice(dot + 1) : stripped;
403
+ }
404
+ function soleColumn(targets) {
405
+ const names = targets.split(",").map(bareIdentifier);
406
+ if (names.length !== 1) return void 0;
407
+ const [name] = names;
408
+ return PLAIN_IDENTIFIER.test(name) ? name : void 0;
409
+ }
410
+ function columnFromText(text) {
411
+ const sqlite = SQLITE_TARGETS.exec(text);
412
+ if (sqlite) {
413
+ const targets = sqlite[1].trim();
414
+ return SQLITE_INDEX_FORM.test(targets) ? void 0 : soleColumn(targets);
415
+ }
416
+ const postgres = POSTGRES_DETAIL_TARGETS.exec(text);
417
+ if (postgres) return soleColumn(postgres[1]);
418
+ return void 0;
419
+ }
420
+ function findUniqueViolationColumn(error, depth) {
421
+ if (error === null || error === void 0 || depth > MAX_CAUSE_DEPTH) return void 0;
422
+ if (typeof error === "string") return columnFromText(error);
423
+ if (typeof error !== "object") return void 0;
424
+ const err = error;
425
+ if (typeof err.message === "string") {
426
+ const fromMessage = columnFromText(err.message);
427
+ if (fromMessage !== void 0) return fromMessage;
428
+ }
429
+ if (typeof err.detail === "string") {
430
+ const fromDetail = columnFromText(err.detail);
431
+ if (fromDetail !== void 0) return fromDetail;
432
+ }
433
+ return findUniqueViolationColumn(err.cause, depth + 1);
434
+ }
435
+ function uniqueViolationColumn(error) {
436
+ if (!isUniqueViolationError(error)) return void 0;
437
+ return findUniqueViolationColumn(error, 0);
438
+ }
439
+
440
+ // src/unbacked-conflict-target.ts
441
+ var UNBACKED_CONFLICT_TARGET = {
442
+ message: /ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint|there is no unique or exclusion constraint matching the ON CONFLICT specification/i
443
+ };
444
+ var MAX_CAUSE_DEPTH2 = 4;
445
+ function isUnbackedConflictTargetError(error) {
446
+ return matchesUnbackedConflictTarget(error, 0);
447
+ }
448
+ function matchesUnbackedConflictTarget(error, depth) {
449
+ if (error === null || error === void 0 || depth > MAX_CAUSE_DEPTH2) return false;
450
+ if (typeof error === "string") return UNBACKED_CONFLICT_TARGET.message.test(error);
451
+ if (typeof error !== "object") return false;
452
+ const err = error;
453
+ if (typeof err.message === "string" && UNBACKED_CONFLICT_TARGET.message.test(err.message)) return true;
454
+ return matchesUnbackedConflictTarget(err.cause, depth + 1);
455
+ }
456
+
283
457
  // src/unique-scope-install-gate.ts
284
458
  var import_security2 = require("@objectstack/spec/security");
285
459
  var SYS_OBJECT_PREFIXES = ["sys_", "base_"];
@@ -382,6 +556,7 @@ function postureGatesGlobalUniques(posture) {
382
556
  GLOBAL_UNIQUE_CONFIRMATION_REQUIRED,
383
557
  GLOBAL_UNIQUE_ISOLATED_PRESCRIPTION,
384
558
  INTERNAL_ERROR_MESSAGE,
559
+ VALIDATION_FAILED_STATUS,
385
560
  _resetEnvDeprecationWarnings,
386
561
  buildGlobalUniqueStopMessage,
387
562
  collectConfiguredLocales,
@@ -391,12 +566,17 @@ function postureGatesGlobalUniques(posture) {
391
566
  describeGlobalUniqueFinding,
392
567
  emitDegradedBootBanner,
393
568
  fieldUniqueIsGlobal,
569
+ fieldsFromZodIssues,
394
570
  globalUniqueFindingId,
395
571
  isMcpServerEnabled,
396
572
  isModuleNotFoundError,
397
573
  isPlatformOwnedObject,
574
+ isRelationSubObjectPhrase,
575
+ isUnbackedConflictTargetError,
576
+ isUniqueViolationError,
398
577
  keysetWalk,
399
578
  looksLikeInternalErrorLeak,
579
+ matchMissingColumnOfRelation,
400
580
  postureGatesGlobalUniques,
401
581
  readEnvWithDeprecation,
402
582
  recordGlobalUniqueAttestation,
@@ -409,9 +589,13 @@ function postureGatesGlobalUniques(posture) {
409
589
  resolveSandboxTimeoutMs,
410
590
  resolveSearchPinyinEnabled,
411
591
  resolveTenancyPosture,
592
+ resolveThrownHttpError,
412
593
  sendError,
413
594
  sendOk,
414
595
  stampSearchPinyinEnabled,
415
- unconfirmedGlobalUniques
596
+ unconfirmedGlobalUniques,
597
+ uniqueViolationColumn,
598
+ validationFailure,
599
+ validationFailureDetails
416
600
  });
417
601
  //# sourceMappingURL=index.js.map