@ai-matrx/records 0.3.3 → 0.5.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,33 +1,83 @@
1
1
  # Changelog — @ai-matrx/records
2
2
 
3
- ## 0.3.3
3
+ ## 0.5.1
4
4
 
5
- Lets a legitimate competing live-store verification finish instead of treating
6
- five seconds of advisory-lock contention as a package failure, while retaining
7
- a hard statement timeout for genuinely abandoned transactions.
5
+ Refreshes the generated store contract to the live 240-door registry caught by
6
+ the blocking release guard after 0.5.0 was tagged.
8
7
 
9
8
  ### Consumer action
10
9
 
11
- None. This changes only the release-time real-store verification lane.
12
-
13
- ## 0.3.2
14
-
15
- Refreshes the generated store contract to the live 238-door, 32-knob registry
16
- and serializes the shared record-store release lane with `records-ui`. This
17
- prevents either package's live verification from observing the other suite's
18
- temporary store state.
10
+ None. The two new door entries are additive generated contract data.
11
+
12
+ ## 0.5.0
13
+
14
+ **Two refusal classes the store really raises, which this package answered with
15
+ "something this package does not recognise".** Both were found by walking the
16
+ real product on 2026-09-19, and in both the screen showed the store's own
17
+ sentence honest, but the class was nameable and this package had not named it.
18
+
19
+ - **`57014` — the store cancelled the statement at its own timeout.** Seen on an
20
+ organization whose Table kernel is large enough for `custom.read_records` to
21
+ run out of time. It is now `timed_out`, which is, with `stale_read`, one of
22
+ only two refusals in the vocabulary worth REPEATING: nothing was refused on
23
+ its merits and nothing was written, so a screen can offer "try again" honestly
24
+ instead of showing a failure a person cannot act on.
25
+ - **`22P02` — a value that does not fit the type the store holds for it.** Seen
26
+ as `invalid input syntax for type uuid: "open"`, writing the text `open` into
27
+ an organization's copy of a package Table whose `status` Field was uuid-shaped.
28
+ It is `invalid_argument`, which is what it is: the CALL is wrong for the shape
29
+ the store holds, and the store's own message names the value and the type.
30
+
31
+ Both are proved against the LIVE store in `src/__tests__/real-doors.test.ts`:
32
+ the SQLSTATEs are raised by the real database inside the suite's own
33
+ rolled-back transaction and read back out of psql's verbose report, and the only
34
+ thing asserted about this package is what it makes of them. Each case is RED
35
+ without its map entry (`expected 'internal' to be 'timed_out'`).
19
36
 
20
37
  ### Consumer action
21
38
 
22
- None. The new door and knob entries are additive generated contract data.
39
+ `RecordsErrorCode` gains one member, `timed_out`. A host that switches
40
+ exhaustively over the union must add an arm for it; every other host needs
41
+ nothing beyond taking `latest`. A host that was special-casing `internal` to
42
+ recognise a timeout by its message text should branch on `timed_out` instead.
23
43
 
24
- ## 0.3.1
44
+ ## 0.4.1
25
45
 
26
- The real-store verification lane now takes one transaction-scoped advisory lock before it
27
- opens the record-store feature knob. Parallel package and UI verification waits its turn
28
- instead of converting unrelated knob contention into a false store refusal.
46
+ Carries the live Supabase adapter proof through the shared npm publishing lane
47
+ by supplying its existing main-database HTTP URL and publishable-key secrets to
48
+ the verification step.
49
+
50
+ ### Consumer action
29
51
 
30
- **Consumer action:** none.
52
+ None beyond taking `latest`; the 0.4.0 API is unchanged.
53
+
54
+ ## 0.4.0
55
+
56
+ **The browser can reach the store again — `supabaseDataSource`.** Every screen
57
+ of the record store was dead from a browser, for everybody, and the cause was a
58
+ single silent drop. `RecordsDataSource.rpc(fn, args, { schema })` says the
59
+ schema out loud, because every door is `custom.<name>` and every trust door is
60
+ `iam.<name>`. supabase-js's own `rpc()` has NO schema option — its third
61
+ argument is `{ head, get, count }` — so a host that bound its client as a bare
62
+ structural cast handed the option to a function that dropped it, every door call
63
+ went to PostgREST's default profile, and the store answered, correctly and
64
+ uselessly, `PGRST202 — Could not find the function public.table_kernel_id in the
65
+ schema cache`. Independent verdict, 2026-09-19.
66
+
67
+ The fix is ONE adapter in the headless core, exported from `/core`, that every
68
+ host inherits — never three hosts (web, desktop, extension) each remembering the
69
+ same thing. It routes through `client.schema(name).rpc(...)`, which is the one
70
+ way supabase-js offers, and it refuses two things by name rather than sending
71
+ them: a call with no schema (which would reach `public`), and a client with no
72
+ `.schema()` method (which would throw inside a render).
73
+
74
+ **The test that would have caught it**, `src/__tests__/supabase-adapter.test.ts`:
75
+ a real supabase-js client against the MAIN database, signed in as `test@test.com`,
76
+ calling a real door. A raw `fetch` to `/rest/v1/rpc/...` would NOT have caught
77
+ this — the bug is in supabase-js's option handling, so only a real client
78
+ exercises it. The RED half reconstructs the bare cast that shipped and asserts
79
+ the store's own PGRST202 naming `public`; the GREEN half is the same call
80
+ through the adapter answering the kernel id.
31
81
 
32
82
  ## 0.3.0
33
83
 
@@ -90,6 +90,7 @@ __export(core_exports, {
90
90
  predictValueRefusals: () => predictValueRefusals,
91
91
  predictWriteRefusals: () => predictWriteRefusals,
92
92
  staleWriteDetail: () => staleWriteDetail,
93
+ supabaseDataSource: () => supabaseDataSource,
93
94
  toAoa: () => toAoa
94
95
  });
95
96
  module.exports = __toCommonJS(core_exports);
@@ -281,6 +282,8 @@ var STORE_DOORS = [
281
282
  { name: "doc_unresolved_tokens", args: "p_organization_id uuid, p_table_id uuid, p_body text", returns: "TABLE(raw text, field_id uuid, why text)", security: "invoker" },
282
283
  { name: "doors_not_deciding_the_caller", args: "", returns: "TABLE(function_name text, identity_args text)", security: "invoker" },
283
284
  { name: "doors_not_deciding_the_record", args: "", returns: "TABLE(function_name text, identity_args text, why text)", security: "invoker" },
285
+ { name: "doors_not_on_one_ladder", args: "", returns: "TABLE(function_name text, identity_args text, why text)", security: "invoker" },
286
+ { name: "effective_level", args: "p_user_id uuid, p_organization_id uuid, p_id uuid, p_type text DEFAULT 'record'::text", returns: "permission_level", security: "definer" },
284
287
  { name: "export_records", args: "p_organization_id uuid, p_table_id uuid, p_limit integer DEFAULT 1000", returns: "TABLE(id uuid, document jsonb)", security: "invoker" },
285
288
  { name: "external_foreign_table_findings", args: "", returns: "SETOF text", security: "definer" },
286
289
  { name: "external_history_event", args: "p_organization_id uuid, p_link_id uuid, p_operation text", returns: "bigint", security: "definer" },
@@ -1712,6 +1715,13 @@ var BY_SQLSTATE = {
1712
1715
  "23514": "refused_by_rule",
1713
1716
  "22004": "invalid_argument",
1714
1717
  "22023": "invalid_argument",
1718
+ // 22P02 — a value does not fit the type the store holds for it. The class is
1719
+ // real and was hit live: an organization whose copy of a package table grew
1720
+ // `status` as a uuid-valued Field was sent the text "open", and the store
1721
+ // answered `invalid input syntax for type uuid: "open"`. It is the CALL that
1722
+ // is wrong for the shape the store holds, which is what `invalid_argument`
1723
+ // already means — the store's own sentence names the value and the type.
1724
+ "22P02": "invalid_argument",
1715
1725
  "02000": "not_found",
1716
1726
  "23503": "missing_reference",
1717
1727
  "23505": "already_exists",
@@ -1723,6 +1733,13 @@ var BY_SQLSTATE = {
1723
1733
  // they just lost access to. It is retryable on a fresher connection, which is
1724
1734
  // a different instruction from every other refusal here.
1725
1735
  "40001": "stale_read",
1736
+ // 57014 — the store cancelled the statement at its own timeout. Like
1737
+ // `stale_read` and unlike every other refusal here, the call was never
1738
+ // refused on its merits: the same call on a smaller page, or once the
1739
+ // organization's kernel is not being rewritten, can succeed. It gets its own
1740
+ // code so a screen can offer "try again" honestly instead of showing a
1741
+ // failure sentence the caller cannot act on.
1742
+ "57014": "timed_out",
1726
1743
  // PostgREST's own two: the store is not being SERVED, which is a different
1727
1744
  // thing from the store refusing. `custom` absent from `pgrst.db_schemas`, or
1728
1745
  // the grants not copied, land here.
@@ -2575,6 +2592,42 @@ function asWriteConflict(error, recordId) {
2575
2592
  };
2576
2593
  }
2577
2594
 
2595
+ // src/core/supabase.ts
2596
+ function refuse(code, message, hint) {
2597
+ return { data: null, error: { code, message, hint, details: "" } };
2598
+ }
2599
+ function supabaseDataSource(client) {
2600
+ const supabase = client;
2601
+ const hasSchema = typeof supabase.schema === "function";
2602
+ return {
2603
+ rpc(fn, args, options) {
2604
+ if (!hasSchema) {
2605
+ return Promise.resolve(
2606
+ refuse(
2607
+ "data_source_unbound",
2608
+ `The records client was given something that is not a supabase client: it has no schema() method, so the door ${fn} cannot be called in the schema it lives in.`,
2609
+ "Pass a supabase-js v2 client (createClient(...)) to supabaseDataSource()."
2610
+ )
2611
+ );
2612
+ }
2613
+ const name = options?.schema;
2614
+ if (!name) {
2615
+ return Promise.resolve(
2616
+ refuse(
2617
+ "schema_unsaid",
2618
+ `The door ${fn} was called without saying which schema it lives in. Every door of this store is custom.${fn} and every trust door is iam.${fn}; an unqualified call reaches PostgREST's default profile (public) and is refused there.`,
2619
+ "This is a bug in the caller, not in your data: /core says the schema on every call."
2620
+ )
2621
+ );
2622
+ }
2623
+ return supabase.schema(name).rpc(fn, args);
2624
+ },
2625
+ schema(name) {
2626
+ return supabase.schema(name);
2627
+ }
2628
+ };
2629
+ }
2630
+
2578
2631
  // src/core/validation.ts
2579
2632
  var DEFAULT_VALUE_MAX_BYTES = 1e5;
2580
2633
  var DEFAULT_DOCUMENT_MAX_BYTES = 1048576;