@shivaedev/sql 0.26.0 → 0.26.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.26.1 - 2026-10-30
4
+
5
+ ### Fixed
6
+
7
+ - `mapSqlError` maps constraints whose declared names exceed PostgreSQL's 63-byte identifier limit. It matches the name PostgreSQL stores, clipped to 63 bytes at a character boundary, instead of sending those violations to the fallback.
8
+
3
9
  ## 0.26.0 - 2026-10-30
4
10
 
5
11
  ### Added
package/README.md CHANGED
@@ -634,7 +634,7 @@ const onSqlError = mapSqlError({
634
634
  const insertNamedLine = lines.insert({ amount: 52, name: "Consulting", note: null }).pipe(transact({ onSqlError }));
635
635
  ```
636
636
 
637
- The mapper handles named unique violations and PostgreSQL foreign-key and check violations. Every other `SqlError`, including unknown constraints, goes to `fallback`. Each callback receives the original error. The inferred error type contains exactly the mapped errors and the fallback; `transact` also preserves other typed failures from the operation. Constraint names belong to the application's migration; error types and presentation belong to the application.
637
+ The mapper handles named unique violations and PostgreSQL foreign-key and check violations. Every other `SqlError`, including unknown constraints, goes to `fallback`. A constraint name longer than PostgreSQL's 63-byte identifier limit matches the clipped name PostgreSQL stores and reports. Each callback receives the original error. The inferred error type contains exactly the mapped errors and the fallback; `transact` also preserves other typed failures from the operation. Constraint names belong to the application's migration; error types and presentation belong to the application.
638
638
 
639
639
  Recover outside `transact` so earlier writes roll back before handling a mapped domain error. A losing unique-key insert can then catch `NameTaken` and read the winner's row without a private transaction error. See the [constraint-failures guide](https://github.com/ShivaeDev/platform/blob/main/docs/framework/constraint-errors.md).
640
640
 
@@ -1,4 +1,5 @@
1
1
  export function mapSqlError(options) {
2
+ const handlers = new Map(Object.entries(options.constraints).map(([name, handler]) => [storedIdentifier(name), handler]));
2
3
  return (error) => {
3
4
  const reason = error.reason;
4
5
  let constraint;
@@ -14,9 +15,22 @@ export function mapSqlError(options) {
14
15
  && typeof reason.cause.constraint === "string") {
15
16
  constraint = reason.cause.constraint;
16
17
  }
17
- return constraint !== undefined && Object.hasOwn(options.constraints, constraint)
18
- ? (options.constraints[constraint] ?? options.fallback)(error)
19
- : options.fallback(error);
18
+ return ((constraint === undefined ? undefined : handlers.get(constraint)) ?? options.fallback)(error);
20
19
  };
21
20
  }
21
+ const IDENTIFIER_BYTES = 63;
22
+ const encoder = new TextEncoder();
23
+ // PostgreSQL stores identifiers clipped to 63 bytes at a character boundary, and reports that clipped name.
24
+ function storedIdentifier(name) {
25
+ let bytes = 0;
26
+ let stored = "";
27
+ for (const character of name) {
28
+ bytes += encoder.encode(character).length;
29
+ if (bytes > IDENTIFIER_BYTES) {
30
+ return stored;
31
+ }
32
+ stored += character;
33
+ }
34
+ return stored;
35
+ }
22
36
  //# sourceMappingURL=mapSqlError.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mapSqlError.js","sourceRoot":"","sources":["../src/mapSqlError.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,WAAW,CAAC,OAG3B;IACA,OAAO,CAAC,KAAe,EAAE,EAAE;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,UAA8B,CAAC;QACnC,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YACvC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAChC,CAAC;aAAM,IACN,MAAM,CAAC,IAAI,KAAK,iBAAiB;eAC9B,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;eAChC,MAAM,CAAC,KAAK,KAAK,IAAI;eACrB,MAAM,IAAI,MAAM,CAAC,KAAK;eACtB,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;eAChE,YAAY,IAAI,MAAM,CAAC,KAAK;eAC5B,OAAO,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,EAC7C,CAAC;YACF,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;QACtC,CAAC;QACD,OAAO,UAAU,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC;YAChF,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;YAC9D,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"mapSqlError.js","sourceRoot":"","sources":["../src/mapSqlError.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,WAAW,CAAC,OAG3B;IACA,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1H,OAAO,CAAC,KAAe,EAAE,EAAE;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,UAA8B,CAAC;QACnC,IAAI,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YACvC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QAChC,CAAC;aAAM,IACN,MAAM,CAAC,IAAI,KAAK,iBAAiB;eAC9B,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;eAChC,MAAM,CAAC,KAAK,KAAK,IAAI;eACrB,MAAM,IAAI,MAAM,CAAC,KAAK;eACtB,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;eAChE,YAAY,IAAI,MAAM,CAAC,KAAK;eAC5B,OAAO,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,EAC7C,CAAC;YACF,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;QACtC,CAAC;QACD,OAAO,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC;IACvG,CAAC,CAAC;AACH,CAAC;AAED,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC,4GAA4G;AAC5G,SAAS,gBAAgB,CAAC,IAAY;IACrC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;QAC9B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;QAC1C,IAAI,KAAK,GAAG,gBAAgB,EAAE,CAAC;YAC9B,OAAO,MAAM,CAAC;QACf,CAAC;QACD,MAAM,IAAI,SAAS,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/sql",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
4
4
  "description": "Schema-derived repositories over native Effect SQL",
5
5
  "keywords": [
6
6
  "effect"
@@ -50,9 +50,9 @@
50
50
  "@effect/platform-node": "4.0.6",
51
51
  "@effect/sql-pg": "4.0.6",
52
52
  "@effect/sql-sqlite-node": "4.0.6",
53
- "@shivaedev/quality": "0.46.0",
54
- "@shivaedev/test-sql": "0.12.0",
55
- "@shivaedev/test-story": "0.25.0",
53
+ "@shivaedev/quality": "0.47.0",
54
+ "@shivaedev/test-sql": "0.13.0",
55
+ "@shivaedev/test-story": "0.25.1",
56
56
  "@types/node": "24.19.2",
57
57
  "effect": "4.0.6",
58
58
  "typescript": "7.0.2",
@@ -8,6 +8,7 @@ export function mapSqlError(options: {
8
8
  readonly constraints: Readonly<Record<string, (error: SqlError) => unknown>>;
9
9
  readonly fallback: (error: SqlError) => unknown;
10
10
  }) {
11
+ const handlers = new Map(Object.entries(options.constraints).map(([name, handler]) => [storedIdentifier(name), handler]));
11
12
  return (error: SqlError) => {
12
13
  const reason = error.reason;
13
14
  let constraint: string | undefined;
@@ -24,8 +25,23 @@ export function mapSqlError(options: {
24
25
  ) {
25
26
  constraint = reason.cause.constraint;
26
27
  }
27
- return constraint !== undefined && Object.hasOwn(options.constraints, constraint)
28
- ? (options.constraints[constraint] ?? options.fallback)(error)
29
- : options.fallback(error);
28
+ return ((constraint === undefined ? undefined : handlers.get(constraint)) ?? options.fallback)(error);
30
29
  };
31
30
  }
31
+
32
+ const IDENTIFIER_BYTES = 63;
33
+ const encoder = new TextEncoder();
34
+
35
+ // PostgreSQL stores identifiers clipped to 63 bytes at a character boundary, and reports that clipped name.
36
+ function storedIdentifier(name: string) {
37
+ let bytes = 0;
38
+ let stored = "";
39
+ for (const character of name) {
40
+ bytes += encoder.encode(character).length;
41
+ if (bytes > IDENTIFIER_BYTES) {
42
+ return stored;
43
+ }
44
+ stored += character;
45
+ }
46
+ return stored;
47
+ }