@ai-matrx/entity-data 0.3.44 → 0.4.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/CHANGELOG.md CHANGED
@@ -6,6 +6,21 @@ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
6
  `git diff npm/entity-data/v0.3.43..npm/entity-data/v0.3.44 -- apps/shared/entity-data`).
7
7
  No source changes intended and no consumer action required.
8
8
 
9
+ ## 0.4.0
10
+
11
+ **Deprecated — moved into `@ai-matrx/records` as `records/entity`.** This version only re-exports
12
+ `@ai-matrx/records/entity` (from `.`, `/core`, `/port`) and `@ai-matrx/records/entity/react` (from `/react`).
13
+ Same API, no behaviour change.
14
+
15
+ **Consumer action:** import `@ai-matrx/records/entity` / `@ai-matrx/records/entity/react` instead and
16
+ remove this dependency.
17
+
18
+ ## 0.3.43
19
+
20
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
21
+ `git diff npm/entity-data/v0.3.42..npm/entity-data/v0.3.43 -- apps/shared/entity-data`).
22
+ No source changes intended and no consumer action required.
23
+
9
24
  ## 0.3.42
10
25
 
11
26
  Automatic changed-only republish (docs/metadata drift since the last tag — see
package/README.md CHANGED
@@ -1,54 +1,10 @@
1
- # @ai-matrx/entity-data
1
+ # @ai-matrx/entity-data — deprecated
2
2
 
3
- Platform records (contacts, projects, tasks…) through the same `DataPort` an Applet reads a custom
4
- table with. A thin typed client over the store's entity doors, and a host `DataPort` for entity
5
- aliases, and token-first React hooks over records' one data engine (`@ai-matrx/records/data/react`).
3
+ Moved into `@ai-matrx/records` as the `entity` section. Same API; change the import:
6
4
 
7
- **Reads and writes.** `create` saves into the active organization the host names (none → refused
8
- `organization_required`), `update` sends the version the person saw (a stale one is refused with the
9
- store's sentence), `archive` / `restore` move a row to the trash and back. **Lists are live** when the host passes `realtime: createEntityRealtime({ manager })`
10
- (and `organization`, the active one): other people's changes arrive as `rows:<alias>` events naming the ids.
11
-
12
- ```ts
13
- import { createEntityDataPort } from "@ai-matrx/entity-data/port";
14
- import { DataPortProvider, hostEvents, useDataRows } from "@ai-matrx/records/data/react";
15
-
16
- const port = createEntityDataPort({
17
- dataSource: supabase, // the signed-in person's supabase-js client
18
- aliases: { contacts: { kind: "entity", token: "party", organizationId } },
19
- });
20
-
21
- // in React:
22
- // <DataPortProvider port={port} events={hostEvents(port)} portKey="applet-1" queryClient={qc}>
23
- // const { rows, hasMore, loadMore, error } = useDataRows("contacts", { org: organizationId, sort: [{ column: "display_name" }] });
24
- ```
25
-
26
- In a React app, name the type and let the engine do the rest:
27
-
28
- ```tsx
29
- import { EntityDataProvider, useEntityList, useEntityWrite } from "@ai-matrx/entity-data/react";
30
-
31
- // once, high: <EntityDataProvider dataSource={supabase} activeOrganizationId={activeOrg}>…
32
- const contacts = useEntityList("party", { where: [{ column: "last_name", op: "eq", value: "Ellis" }] });
33
- const write = useEntityWrite("party");
34
- await write.update(contacts.rows[0]._id, { first_name: "Ada" }); // drawn at once, rolled back on refusal
35
- ```
36
-
37
- ## Entries
38
-
39
- | Entry | What |
5
+ | Was | Now |
40
6
  |---|---|
41
- | `@ai-matrx/entity-data/core` | `createEntityClient({ dataSource })` → `list`, `get`, `columns`, `insert`, `update`, `archive`, `unarchive`; `toWire`; `mapEntityError` |
42
- | `@ai-matrx/entity-data/port` | `createEntityDataPort({ client \| dataSource, aliases, activeOrganizationId })` → `HostDataPort` + `describe` |
43
- | `@ai-matrx/entity-data/react` | `EntityDataProvider`, `useEntityList`, `useEntityColumns`, `useEntityType`, `useEntityRow`, `useEntityWrite` |
44
- | `@ai-matrx/entity-data` | both of the above |
45
-
46
- ## Behaviour worth knowing
47
-
48
- - The lane defaults to the type's own landing lane, decided by the store; `org` is a filter and never a lock.
49
- - Paging is keyset: the cursor is the store's `next_after`.
50
- - A filter or sort the store does not offer yet is refused by name with the store's sentence, never emulated in the browser.
51
- - Custom fields read from `row._custom` and filter as `_custom.<key>`; a field the person may not read arrives `null`.
52
- - A total that stopped at the store's count cap is reported as `null` (unknown), never as a wrong number.
7
+ | `@ai-matrx/entity-data`, `/core`, `/port` | `@ai-matrx/records/entity` |
8
+ | `@ai-matrx/entity-data/react` | `@ai-matrx/records/entity/react` |
53
9
 
54
- See `FEATURE.md` for the local mechanics.
10
+ This package only re-exports those sections and will be marked deprecated on npm.
@@ -3,10 +3,6 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
6
  var __copyProps = (to, from, except, desc) => {
11
7
  if (from && typeof from === "object" || typeof from === "function") {
12
8
  for (let key of __getOwnPropNames(from))
@@ -15,381 +11,11 @@ var __copyProps = (to, from, except, desc) => {
15
11
  }
16
12
  return to;
17
13
  };
14
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
18
15
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
16
 
20
17
  // src/core/index.ts
21
18
  var core_exports = {};
22
- __export(core_exports, {
23
- CUSTOM_PREFIX: () => CUSTOM_PREFIX,
24
- DEFAULT_BATCH_MS: () => DEFAULT_BATCH_MS,
25
- DEFAULT_MAX_ORG_TOPICS: () => DEFAULT_MAX_ORG_TOPICS,
26
- DEFAULT_MAX_WAIT_MS: () => DEFAULT_MAX_WAIT_MS,
27
- ENTITY_DOOR_SCHEMA: () => ENTITY_DOOR_SCHEMA,
28
- ENTITY_NOTICE_EVENT: () => ENTITY_NOTICE_EVENT,
29
- ENTITY_REFUSALS: () => ENTITY_REFUSALS,
30
- OP_ID_HEADER: () => OP_ID_HEADER,
31
- createEntityClient: () => createEntityClient,
32
- createEntityRealtime: () => createEntityRealtime,
33
- entityNoticeKey: () => entityNoticeKey,
34
- entityOrgChannel: () => entityOrgChannel,
35
- entityOrgChannelSpec: () => entityOrgChannelSpec,
36
- entityOrgTopic: () => entityOrgTopic,
37
- mapEntityError: () => mapEntityError,
38
- orderOrganizationTopics: () => orderOrganizationTopics,
39
- readEntityRealtimeKnobs: () => readEntityRealtimeKnobs,
40
- refusal: () => refusal,
41
- toWire: () => toWire,
42
- wireColumn: () => wireColumn
43
- });
44
19
  module.exports = __toCommonJS(core_exports);
45
-
46
- // src/core/client.ts
47
- var import_core2 = require("@ai-matrx/records/core");
48
-
49
- // src/core/errors.ts
50
- var import_core = require("@ai-matrx/records/core");
51
- var ENTITY_REFUSALS = {
52
- /** conflict_version: somebody else's write won. */
53
- MX001: "stale_write",
54
- /** organization_required: the call needs an organization and carried none. */
55
- MX002: "organization_required",
56
- /** forbidden. */
57
- MX003: "door",
58
- /** not_found (also: not visible to this person). */
59
- MX004: "not_found",
60
- /** validation_refused (detail.reason required / bad_value / ambiguous_word / constraint; `not_supported` is read apart below). */
61
- MX005: "invalid_argument",
62
- /** system_column: a write named a column the store owns. */
63
- MX008: "invalid_argument",
64
- /** unknown_identifier: a column that is not there or not usable here. */
65
- MX012: "invalid_argument",
66
- /** cursor_invalid: the page cursor belongs to another list; the engine restarts the page. */
67
- MX013: "invalid_argument",
68
- /** view_not_offered. */
69
- MX015: "invalid_argument",
70
- /** invalid_argument: a parameter the door refuses by name. */
71
- MX016: "invalid_argument"
72
- };
73
- function mapEntityError(error, where) {
74
- const mapped = (0, import_core.mapPgError)(error, where);
75
- const state = mapped.sqlstate;
76
- if (state && /^MX\d{3}$/.test(state)) {
77
- const code = ENTITY_REFUSALS[state];
78
- if (code) {
79
- const reason = mapped.detail?.reason;
80
- const known = { ...mapped, code: state === "MX005" && reason === "not_supported" ? "not_supported" : code };
81
- delete known.unmapped;
82
- delete known.diagnostic;
83
- return known;
84
- }
85
- }
86
- return mapped;
87
- }
88
- function refusal(code, message, hint) {
89
- return { ok: false, error: { code, message, ...hint ? { hint } : {} } };
90
- }
91
-
92
- // src/core/client.ts
93
- var OP_ID_HEADER = "x-matrx-op-id";
94
- var ENTITY_DOOR_SCHEMA = "platform";
95
- function createEntityClient(options) {
96
- const source = (0, import_core2.asRecordsDataSource)(options.dataSource ?? options.supabase);
97
- let warnedHeaderless = false;
98
- function withHeaders(pending, headers) {
99
- if (!headers) return;
100
- const builder = pending;
101
- if (typeof builder.setHeader === "function") {
102
- for (const [name, value] of Object.entries(headers)) builder.setHeader(name, value);
103
- return;
104
- }
105
- if (!warnedHeaderless) {
106
- warnedHeaderless = true;
107
- console.warn(
108
- `[@ai-matrx/entity-data] This data source cannot carry a per-call header (${Object.keys(headers).join(", ")}), so this browser re-reads the notice of its own writes once.`
109
- );
110
- }
111
- }
112
- async function call(fn, args, schema = ENTITY_DOOR_SCHEMA, headers) {
113
- try {
114
- const pending = source.rpc(fn, args, { schema });
115
- withHeaders(pending, headers);
116
- const answer = await pending;
117
- if (answer.error) return { ok: false, error: mapEntityError(answer.error, `${schema}.${fn}`) };
118
- return { ok: true, data: answer.data };
119
- } catch (thrown) {
120
- return {
121
- ok: false,
122
- error: mapEntityError({ code: "", message: thrown instanceof Error ? thrown.message : String(thrown) }, `${schema}.${fn}`)
123
- };
124
- }
125
- }
126
- return {
127
- list(request) {
128
- const args = {
129
- p_entity_type: request.token,
130
- p_scope: request.scope ?? null,
131
- p_filter: request.filter ?? {},
132
- p_search: request.search ?? null,
133
- p_sort: request.sort ?? null,
134
- p_archived: request.archived ?? "active",
135
- p_page_size: request.pageSize ?? null,
136
- p_after: request.after ?? null,
137
- p_with_total: request.withTotal ?? true
138
- };
139
- return call("entity_list_scoped", args);
140
- },
141
- get(request) {
142
- return call("entity_get", { p_entity_type: request.token, p_ids: [...request.ids] });
143
- },
144
- columns(request) {
145
- return call("entity_columns", { p_organization_id: request.organizationId ?? null, p_token: request.token });
146
- },
147
- insert(request) {
148
- return call(
149
- "entity_insert",
150
- { p_entity_type: request.token, p_organization_id: request.organizationId, p_rows: request.rows.map((r) => ({ ...r })) },
151
- ENTITY_DOOR_SCHEMA,
152
- opHeader(request.opId)
153
- );
154
- },
155
- update(request) {
156
- return call(
157
- "entity_update",
158
- { p_entity_type: request.token, p_id: request.id, p_base_version: request.baseVersion, p_patch: request.patch },
159
- ENTITY_DOOR_SCHEMA,
160
- opHeader(request.opId)
161
- );
162
- },
163
- archive(request) {
164
- return call("entity_archive", { p_entity_type: request.token, p_ids: [...request.ids] }, ENTITY_DOOR_SCHEMA, opHeader(request.opId));
165
- },
166
- unarchive(request) {
167
- return call("entity_unarchive", { p_entity_type: request.token, p_ids: [...request.ids] }, ENTITY_DOOR_SCHEMA, opHeader(request.opId));
168
- },
169
- knob(request) {
170
- return call("knob_resolve", { p_feature: "entity_data", p_key: request.key, p_organization_id: request.organizationId ?? null });
171
- },
172
- async memberOrganizations() {
173
- const answer = await call("my_orgs", {}, "iam");
174
- if (!answer.ok) return answer;
175
- const rows = Array.isArray(answer.data) ? answer.data : [];
176
- const ids = rows.map((row) => typeof row === "string" ? row : row && typeof row === "object" ? Object.values(row)[0] : null).filter((id) => typeof id === "string");
177
- return { ok: true, data: ids };
178
- }
179
- };
180
- }
181
- function opHeader(opId) {
182
- return opId ? { [OP_ID_HEADER]: opId } : void 0;
183
- }
184
-
185
- // src/core/wire.ts
186
- var IDENTITY = {
187
- _id: "id",
188
- _version: "version",
189
- _organization_id: "organization_id"
190
- };
191
- var CUSTOM_PREFIX = "_custom.";
192
- function wireColumn(column, customKeys) {
193
- const identity = IDENTITY[column];
194
- if (identity) return identity;
195
- if (column.startsWith(CUSTOM_PREFIX)) return column;
196
- return customKeys.has(column) ? `${CUSTOM_PREFIX}${column}` : column;
197
- }
198
- function predicate(clause, customKeys) {
199
- const column = wireColumn(clause.column, customKeys);
200
- const v = clause.value;
201
- switch (clause.op) {
202
- case "eq":
203
- return v === null || v === void 0 ? { column, op: "empty" } : { column, op: "eq", value: v };
204
- case "neq":
205
- return v === null || v === void 0 ? { column, op: "empty", negated: true } : { column, op: "neq", value: v };
206
- case "lt":
207
- case "lte":
208
- case "gt":
209
- case "gte":
210
- return { column, op: clause.op, value: v };
211
- case "in":
212
- return { column, op: "in", values: Array.isArray(v) ? v : [] };
213
- case "empty":
214
- return v === false ? { column, op: "empty", negated: true } : { column, op: "empty" };
215
- case "contains":
216
- return { column, op: "contains", value: v };
217
- default:
218
- return { column, op: String(clause.op), value: v };
219
- }
220
- }
221
- function toWire(query, customKeys = /* @__PURE__ */ new Set()) {
222
- const q = query ?? {};
223
- const out = {};
224
- const scope = {};
225
- if (q.lane) scope.kind = q.lane;
226
- if (q.org && q.org !== "all") scope.organization_id = q.org;
227
- if (scope.kind || scope.organization_id) out.scope = scope;
228
- if (q.where && q.where.length > 0) out.filter = { all: q.where.map((c) => predicate(c, customKeys)) };
229
- const search = q.search?.trim();
230
- if (search) out.search = search;
231
- if (q.sort && q.sort.length > 0) {
232
- out.sort = q.sort.map((s) => ({
233
- column: wireColumn(s.column, customKeys),
234
- dir: s.direction === "desc" ? "desc" : "asc",
235
- nulls: "last"
236
- }));
237
- }
238
- if (typeof q.pageSize === "number" && Number.isFinite(q.pageSize)) out.pageSize = Math.max(1, Math.floor(q.pageSize));
239
- return out;
240
- }
241
-
242
- // src/core/realtime.ts
243
- var import_realtime = require("@ai-matrx/realtime");
244
- var import_records = require("@ai-matrx/records");
245
- var entityOrgChannel = (0, import_realtime.defineChannelNamespace)({
246
- namespace: "entity-org",
247
- foreignTopic: "mx:entity-org",
248
- parts: ["organizationId", "token"],
249
- description: "One organization's changes to one platform type (contacts, deals, projects, tasks, employees). Carries NOTICES written by platform.entity_org_notice_stmt \u2014 which ids moved, never a value. Admitted by the one RLS policy on realtime.messages to members of that organization."
250
- });
251
- var ENTITY_NOTICE_EVENT = "entity_bump";
252
- var DEFAULT_BATCH_MS = 150;
253
- var DEFAULT_MAX_WAIT_MS = 1e3;
254
- var DEFAULT_MAX_ORG_TOPICS = 20;
255
- function entityNoticeKey(notice) {
256
- return notice.id ?? (notice.at ? `${notice.organization_id ?? ""}:${notice.entity_type ?? ""}:${notice.at}` : void 0);
257
- }
258
- function entityOrgTopic(organizationId, token) {
259
- return entityOrgChannel.topic({ organizationId, token });
260
- }
261
- function entityOrgChannelSpec(organizationId, token, onNotice, onBackfill) {
262
- return {
263
- topic: entityOrgTopic(organizationId, token),
264
- // Database Broadcast is authorized by RLS on realtime.messages; the manager awaits setAuth() first.
265
- private: true,
266
- // The sender is Postgres: no Matrx envelope. Our own write is known by the op id it carried.
267
- wire: { mode: "raw", isOwnMessage: (payload) => (0, import_records.isOwnOp)(payload?.op_id) },
268
- eventKey: (_source, payload) => entityNoticeKey(payload ?? {}),
269
- broadcast: [
270
- {
271
- event: ENTITY_NOTICE_EVENT,
272
- onMessage: (message) => {
273
- const notice = message.data ?? {};
274
- if ((0, import_records.isOwnOp)(notice.op_id)) return;
275
- onNotice(notice);
276
- }
277
- }
278
- ],
279
- // Realtime has no replay: a channel back from a gap re-reads.
280
- onBackfill: () => onBackfill()
281
- };
282
- }
283
- function orderOrganizationTopics(input) {
284
- const members = input.members ? new Set(input.members) : null;
285
- const out = [];
286
- const push = (org) => {
287
- if (!org || out.includes(org)) return;
288
- if (members && !members.has(org)) return;
289
- out.push(org);
290
- };
291
- push(input.active);
292
- push(input.filter);
293
- const held = [...(input.held ?? /* @__PURE__ */ new Map()).entries()].sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
294
- for (const [org] of held) push(org);
295
- for (const org of [...input.members ?? []].sort()) push(org);
296
- return out.slice(0, Math.max(0, input.max));
297
- }
298
- function createEntityRealtime(options = {}) {
299
- const batchMs = options.batchMs ?? DEFAULT_BATCH_MS;
300
- const maxWaitMs = options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;
301
- const channels = /* @__PURE__ */ new Map();
302
- const open = (organizationId, token) => {
303
- const channel = { listeners: /* @__PURE__ */ new Set(), stop: null };
304
- const spec = entityOrgChannelSpec(
305
- organizationId,
306
- token,
307
- (notice) => {
308
- for (const listener of channel.listeners) listener.notice(notice);
309
- },
310
- () => {
311
- for (const listener of channel.listeners) listener.backfill();
312
- }
313
- );
314
- if (options.manager) {
315
- const handle = options.manager.open(spec);
316
- channel.stop = () => handle.close();
317
- } else {
318
- channel.stop = (0, import_realtime.subscribeToRealtimeManager)(() => spec);
319
- }
320
- return channel;
321
- };
322
- return {
323
- subscribe({ token, organizationIds }, sink) {
324
- let timer = null;
325
- let firstAt = 0;
326
- let pending = /* @__PURE__ */ new Set();
327
- let wholeList = false;
328
- const flush = () => {
329
- if (timer !== null) clearTimeout(timer);
330
- timer = null;
331
- const ids = [...pending];
332
- const all = wholeList;
333
- pending = /* @__PURE__ */ new Set();
334
- wholeList = false;
335
- if (all) sink.rows(null);
336
- else if (ids.length > 0) sink.rows(ids);
337
- };
338
- const nudge = () => {
339
- const now = Date.now();
340
- if (timer === null) firstAt = now;
341
- else clearTimeout(timer);
342
- const wait = Math.max(0, Math.min(batchMs, firstAt + maxWaitMs - now));
343
- timer = setTimeout(flush, wait);
344
- };
345
- const listener = {
346
- notice(notice) {
347
- if (notice.entity_type && notice.entity_type !== token) return;
348
- if (notice.ids == null) wholeList = true;
349
- else for (const id of notice.ids) pending.add(id);
350
- nudge();
351
- },
352
- backfill() {
353
- wholeList = true;
354
- flush();
355
- }
356
- };
357
- const keys = [...new Set(organizationIds)].map((org) => {
358
- const key = entityOrgTopic(org, token);
359
- const channel = channels.get(key) ?? open(org, token);
360
- channels.set(key, channel);
361
- channel.listeners.add(listener);
362
- return key;
363
- });
364
- return () => {
365
- if (timer !== null) clearTimeout(timer);
366
- for (const key of keys) {
367
- const channel = channels.get(key);
368
- if (!channel) continue;
369
- channel.listeners.delete(listener);
370
- if (channel.listeners.size > 0) continue;
371
- channel.stop?.();
372
- channels.delete(key);
373
- }
374
- };
375
- }
376
- };
377
- }
378
- async function readEntityRealtimeKnobs(client, organizationId) {
379
- const read = async (key, fallback) => {
380
- const answer = await client.knob({ key, organizationId: organizationId ?? null });
381
- const value = answer.ok ? Number(answer.data) : Number.NaN;
382
- if (!Number.isFinite(value)) {
383
- console.warn(`[@ai-matrx/entity-data] knob entity_data.${key} did not resolve; using its starting value ${fallback}.`);
384
- return fallback;
385
- }
386
- return value;
387
- };
388
- const [batchMs, maxWaitMs, maxOrgTopics] = await Promise.all([
389
- read("realtime_batch_ms", DEFAULT_BATCH_MS),
390
- read("realtime_max_wait_ms", DEFAULT_MAX_WAIT_MS),
391
- read("realtime_max_org_topics", DEFAULT_MAX_ORG_TOPICS)
392
- ]);
393
- return { batchMs, maxWaitMs, maxOrgTopics };
394
- }
20
+ __reExport(core_exports, require("@ai-matrx/records/entity"), module.exports);
395
21
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/index.ts","../../src/core/client.ts","../../src/core/errors.ts","../../src/core/wire.ts","../../src/core/realtime.ts"],"sourcesContent":["// src/core/index.ts — @ai-matrx/entity-data/core\nexport * from \"./types\";\nexport {\n createEntityClient,\n ENTITY_DOOR_SCHEMA,\n OP_ID_HEADER,\n type CreateEntityClientOptions,\n type EntityClient,\n type EntityListRequest,\n type EntityWriteOptions,\n} from \"./client\";\nexport { mapEntityError, refusal, ENTITY_REFUSALS } from \"./errors\";\nexport { toWire, wireColumn, CUSTOM_PREFIX } from \"./wire\";\nexport {\n createEntityRealtime,\n entityOrgChannel,\n entityOrgChannelSpec,\n entityOrgTopic,\n entityNoticeKey,\n orderOrganizationTopics,\n readEntityRealtimeKnobs,\n ENTITY_NOTICE_EVENT,\n DEFAULT_BATCH_MS,\n DEFAULT_MAX_WAIT_MS,\n DEFAULT_MAX_ORG_TOPICS,\n type CreateEntityRealtimeOptions,\n type EntityNotice,\n type EntityNoticeSink,\n type EntityRealtime,\n} from \"./realtime\";\n","// src/core/client.ts — @ai-matrx/entity-data/core\n//\n// THE THIN TYPED CLIENT over the live INVOKER doors in `platform`: reads (`entity_list_scoped`,\n// `entity_get`, `entity_columns`) and, since 0.3, writes (`entity_insert`, `entity_update`,\n// `entity_archive`, `entity_unarchive`). Every call answers a `RecordsResult` and\n// never throws; every refusal is the store's own sentence (see `errors.ts`). The caller's seat is the\n// one on the supabase client handed in — nothing here widens or narrows what the store lets her see.\n\nimport { asRecordsDataSource } from \"@ai-matrx/records/core\";\nimport type { RecordsDataSource, RecordsResult, Uuid } from \"@ai-matrx/records\";\nimport { mapEntityError } from \"./errors\";\nimport type { EntityColumnsAnswer, EntityGetAnswer, EntityListAnswer, EntityWireQuery, EntityWriteAnswer } from \"./types\";\n\n/**\n * THE ECHO HEADER (U6). The entity doors take no `_op_id` argument: the writer's op id rides the request\n * as this header, PostgREST hands it to `request.headers`, and `platform._entity_org_notice` puts it on\n * the notice as `op_id`, so the browser that wrote drops its own echo (`@ai-matrx/records` `isOwnOp`).\n */\nexport const OP_ID_HEADER = \"x-matrx-op-id\";\n\n/** What a write takes beyond its arguments: the op id minted for it (`mintOpId`). */\nexport interface EntityWriteOptions {\n opId?: string;\n}\n\n/** The schema every entity door lives in. */\nexport const ENTITY_DOOR_SCHEMA = \"platform\";\n\nexport interface CreateEntityClientOptions {\n /** A supabase-js v2 client for the signed-in person, or anything with `rpc(fn, args, {schema})`. */\n dataSource?: unknown;\n /** Same thing, spelled the way the plan names it. Give one of the two. */\n supabase?: unknown;\n}\n\nexport interface EntityListRequest extends EntityWireQuery {\n token: string;\n /** Keyset cursor from the previous answer's `next_after`. */\n after?: string | null;\n /** Ask the door to skip the count. Default: counted. */\n withTotal?: boolean;\n archived?: \"active\" | \"archived\" | \"all\";\n}\n\nexport interface EntityClient {\n list(request: EntityListRequest): Promise<RecordsResult<EntityListAnswer>>;\n get(request: { token: string; ids: readonly Uuid[] }): Promise<RecordsResult<EntityGetAnswer>>;\n /** `organizationId` null reads the custom fields of every organization the person belongs to. */\n columns(request: { token: string; organizationId?: Uuid | null }): Promise<RecordsResult<EntityColumnsAnswer>>;\n /** One `entity_data` knob as `platform.knob_resolve` answers it for an organization (live updates read three). */\n knob(request: { key: string; organizationId?: Uuid | null }): Promise<RecordsResult<unknown>>;\n /** `platform.entity_insert`: rows into the organization the host names (never guessed). */\n insert(request: { token: string; organizationId: Uuid; rows: readonly Record<string, unknown>[] } & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;\n /** `platform.entity_update`: one row, refused as stale (`MX001`) unless `baseVersion` is still current. */\n update(request: { token: string; id: Uuid; baseVersion: number; patch: Record<string, unknown> } & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;\n /** `platform.entity_archive` (on these types, the trash). */\n archive(request: { token: string; ids: readonly Uuid[] } & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;\n /** `platform.entity_unarchive`. */\n unarchive(request: { token: string; ids: readonly Uuid[] } & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;\n /** The organizations the person belongs to, as row security counts them (`iam.my_orgs()`: archived ones closed). */\n memberOrganizations(): Promise<RecordsResult<Uuid[]>>;\n}\n\nexport function createEntityClient(options: CreateEntityClientOptions): EntityClient {\n const source: RecordsDataSource = asRecordsDataSource(options.dataSource ?? options.supabase);\n\n let warnedHeaderless = false;\n\n /**\n * A per-call header rides the request builder supabase-js hands back (`PostgrestBuilder.setHeader`, set\n * before the builder is awaited). `@ai-matrx/records`' adapter returns that builder unwrapped, so the\n * header reaches PostgREST on this one call only — never on the client every other caller shares.\n * A data seam whose answer has no `setHeader` (a host's own adapter, a test double) cannot carry it:\n * the write still lands, the writer re-reads its own echo once, and the console says why.\n */\n function withHeaders(pending: PromiseLike<unknown>, headers: Record<string, string> | undefined): void {\n if (!headers) return;\n const builder = pending as { setHeader?: (name: string, value: string) => unknown };\n if (typeof builder.setHeader === \"function\") {\n for (const [name, value] of Object.entries(headers)) builder.setHeader(name, value);\n return;\n }\n if (!warnedHeaderless) {\n warnedHeaderless = true;\n console.warn(\n `[@ai-matrx/entity-data] This data source cannot carry a per-call header (${Object.keys(headers).join(\", \")}), so this browser re-reads the notice of its own writes once.`,\n );\n }\n }\n\n async function call<T>(\n fn: string,\n args: Record<string, unknown>,\n schema: string = ENTITY_DOOR_SCHEMA,\n headers?: Record<string, string>,\n ): Promise<RecordsResult<T>> {\n try {\n const pending = source.rpc(fn, args, { schema });\n withHeaders(pending, headers);\n const answer = await pending;\n if (answer.error) return { ok: false, error: mapEntityError(answer.error, `${schema}.${fn}`) };\n return { ok: true, data: answer.data as T };\n } catch (thrown) {\n // supabase-js answers failures; a throw here is a transport that never reached the store.\n return {\n ok: false,\n error: mapEntityError({ code: \"\", message: thrown instanceof Error ? thrown.message : String(thrown) }, `${schema}.${fn}`),\n };\n }\n }\n\n return {\n list(request) {\n const args: Record<string, unknown> = {\n p_entity_type: request.token,\n p_scope: request.scope ?? null,\n p_filter: request.filter ?? {},\n p_search: request.search ?? null,\n p_sort: request.sort ?? null,\n p_archived: request.archived ?? \"active\",\n p_page_size: request.pageSize ?? null,\n p_after: request.after ?? null,\n p_with_total: request.withTotal ?? true,\n };\n return call<EntityListAnswer>(\"entity_list_scoped\", args);\n },\n get(request) {\n return call<EntityGetAnswer>(\"entity_get\", { p_entity_type: request.token, p_ids: [...request.ids] });\n },\n columns(request) {\n return call<EntityColumnsAnswer>(\"entity_columns\", { p_organization_id: request.organizationId ?? null, p_token: request.token });\n },\n insert(request) {\n return call<EntityWriteAnswer>(\n \"entity_insert\",\n { p_entity_type: request.token, p_organization_id: request.organizationId, p_rows: request.rows.map((r) => ({ ...r })) },\n ENTITY_DOOR_SCHEMA,\n opHeader(request.opId),\n );\n },\n update(request) {\n return call<EntityWriteAnswer>(\n \"entity_update\",\n { p_entity_type: request.token, p_id: request.id, p_base_version: request.baseVersion, p_patch: request.patch },\n ENTITY_DOOR_SCHEMA,\n opHeader(request.opId),\n );\n },\n archive(request) {\n return call<EntityWriteAnswer>(\"entity_archive\", { p_entity_type: request.token, p_ids: [...request.ids] }, ENTITY_DOOR_SCHEMA, opHeader(request.opId));\n },\n unarchive(request) {\n return call<EntityWriteAnswer>(\"entity_unarchive\", { p_entity_type: request.token, p_ids: [...request.ids] }, ENTITY_DOOR_SCHEMA, opHeader(request.opId));\n },\n knob(request) {\n return call<unknown>(\"knob_resolve\", { p_feature: \"entity_data\", p_key: request.key, p_organization_id: request.organizationId ?? null });\n },\n async memberOrganizations() {\n const answer = await call<unknown>(\"my_orgs\", {}, \"iam\");\n if (!answer.ok) return answer;\n const rows = Array.isArray(answer.data) ? answer.data : [];\n // A SETOF uuid comes back as bare strings; a row shape (`{my_orgs}`) is read the same way.\n const ids = rows\n .map((row) => (typeof row === \"string\" ? row : row && typeof row === \"object\" ? Object.values(row as Record<string, unknown>)[0] : null))\n .filter((id): id is string => typeof id === \"string\");\n return { ok: true, data: ids };\n },\n };\n}\n\nfunction opHeader(opId: string | undefined): Record<string, string> | undefined {\n return opId ? { [OP_ID_HEADER]: opId } : undefined;\n}\n","// src/core/errors.ts — @ai-matrx/entity-data/core\n//\n// THE ERROR MAP (PHASE-B-APPLETS §4). The entity doors raise their own `MX0xx` class; this turns it\n// into the `RecordsError` every other door of the records package answers, carrying THE STORE'S\n// SENTENCE VERBATIM (message and hint are never rewritten here). Anything that is not an `MX` code\n// (a timeout, a dropped connection, a revoked schema) goes through records' own `mapPgError`.\n\nimport { mapPgError } from \"@ai-matrx/records/core\";\nimport type { PgError, RecordsError, RecordsErrorCode } from \"@ai-matrx/records\";\n\n/**\n * Every MX code a read door raises, and the engine code a screen branches on. A code the doors learn\n * later and this map lacks comes back `internal` with `unmapped: true` (never silently \"handled\").\n */\nexport const ENTITY_REFUSALS: Readonly<Record<string, RecordsErrorCode>> = {\n /** conflict_version: somebody else's write won. */\n MX001: \"stale_write\",\n /** organization_required: the call needs an organization and carried none. */\n MX002: \"organization_required\",\n /** forbidden. */\n MX003: \"door\",\n /** not_found (also: not visible to this person). */\n MX004: \"not_found\",\n /** validation_refused (detail.reason required / bad_value / ambiguous_word / constraint; `not_supported` is read apart below). */\n MX005: \"invalid_argument\",\n /** system_column: a write named a column the store owns. */\n MX008: \"invalid_argument\",\n /** unknown_identifier: a column that is not there or not usable here. */\n MX012: \"invalid_argument\",\n /** cursor_invalid: the page cursor belongs to another list; the engine restarts the page. */\n MX013: \"invalid_argument\",\n /** view_not_offered. */\n MX015: \"invalid_argument\",\n /** invalid_argument: a parameter the door refuses by name. */\n MX016: \"invalid_argument\",\n};\n\nexport function mapEntityError(error: PgError, where: string): RecordsError {\n const mapped = mapPgError(error, where);\n const state = mapped.sqlstate;\n if (state && /^MX\\d{3}$/.test(state)) {\n const code = ENTITY_REFUSALS[state];\n if (code) {\n // MX005 that says the store does not offer this yet is a different question for a screen than a bad value.\n const reason = (mapped.detail as { reason?: unknown } | undefined)?.reason;\n const known: RecordsError = { ...mapped, code: state === \"MX005\" && reason === \"not_supported\" ? \"not_supported\" : code };\n delete known.unmapped;\n delete known.diagnostic;\n return known;\n }\n }\n return mapped;\n}\n\n/** A refusal this package itself raises (an alias nobody declared, a write not offered yet). */\nexport function refusal(code: RecordsErrorCode, message: string, hint?: string): { ok: false; error: RecordsError } {\n return { ok: false, error: { code, message, ...(hint ? { hint } : {}) } };\n}\n","// src/core/wire.ts — @ai-matrx/entity-data/core\n//\n// `toWire`: the Applets `Query` (§4) → the door's arguments (scope, filter, sort). Pure.\n//\n// What the doors do not offer yet is NOT filtered, reordered or emulated here: the clause is sent as\n// asked and the door refuses it BY NAME (`\"contains\" filters are not offered yet.`), so the day the\n// door learns it the port already speaks it, and until then the person reads the store's sentence.\n// A browser-side filter would silently drop everything the browser was not shown.\n\nimport type { Query, WhereClause } from \"@ai-matrx/records/data\";\nimport type { EntityWirePredicate, EntityWireQuery, EntityWireSort } from \"./types\";\n\n/** The runtime's underscore names for the three identity columns the doors call `id`, `version`, `organization_id`. */\nconst IDENTITY: Readonly<Record<string, string>> = {\n _id: \"id\",\n _version: \"version\",\n _organization_id: \"organization_id\",\n};\n\nexport const CUSTOM_PREFIX = \"_custom.\";\n\n/**\n * Applets column key → the door's identifier. A custom field is `_custom.<key>` (the key as\n * `columns()` answered it — the same plain key for every viewer; `<key>@<organization_id>` is accepted, never produced).\n */\nexport function wireColumn(column: string, customKeys: ReadonlySet<string>): string {\n const identity = IDENTITY[column];\n if (identity) return identity;\n if (column.startsWith(CUSTOM_PREFIX)) return column;\n return customKeys.has(column) ? `${CUSTOM_PREFIX}${column}` : column;\n}\n\nfunction predicate(clause: WhereClause, customKeys: ReadonlySet<string>): EntityWirePredicate {\n const column = wireColumn(clause.column, customKeys);\n const v = clause.value;\n switch (clause.op) {\n case \"eq\":\n // The store reads \"equals nothing\" as empty (the table port's rule, so both kinds of source agree).\n return v === null || v === undefined ? { column, op: \"empty\" } : { column, op: \"eq\", value: v };\n case \"neq\":\n return v === null || v === undefined ? { column, op: \"empty\", negated: true } : { column, op: \"neq\", value: v };\n case \"lt\":\n case \"lte\":\n case \"gt\":\n case \"gte\":\n return { column, op: clause.op, value: v };\n case \"in\":\n return { column, op: \"in\", values: Array.isArray(v) ? v : [] };\n case \"empty\":\n return v === false ? { column, op: \"empty\", negated: true } : { column, op: \"empty\" };\n case \"contains\":\n return { column, op: \"contains\", value: v };\n default:\n // An op this version does not know is still SENT, so the door names it, instead of vanishing here.\n return { column, op: String((clause as WhereClause).op), value: v };\n }\n}\n\nexport function toWire(query: Query | undefined, customKeys: ReadonlySet<string> = new Set()): EntityWireQuery {\n const q = query ?? {};\n const out: EntityWireQuery = {};\n const scope: NonNullable<EntityWireQuery[\"scope\"]> = {};\n if (q.lane) scope.kind = q.lane; // absent = the type's own landing lane, decided by the door\n if (q.org && q.org !== \"all\") scope.organization_id = q.org; // a filter, never a lock\n if (scope.kind || scope.organization_id) out.scope = scope;\n if (q.where && q.where.length > 0) out.filter = { all: q.where.map((c) => predicate(c, customKeys)) };\n const search = q.search?.trim();\n if (search) out.search = search;\n if (q.sort && q.sort.length > 0) {\n out.sort = q.sort.map<EntityWireSort>((s) => ({\n column: wireColumn(s.column, customKeys),\n dir: s.direction === \"desc\" ? \"desc\" : \"asc\",\n nulls: \"last\",\n }));\n }\n if (typeof q.pageSize === \"number\" && Number.isFinite(q.pageSize)) out.pageSize = Math.max(1, Math.floor(q.pageSize));\n return out;\n}\n","// src/core/realtime.ts — @ai-matrx/entity-data/core\n//\n// LIVE NOTICES FOR PLATFORM LISTS (PHASE-B-APPLETS R4 / U6).\n//\n// The database announces every statement on the five Table-API tables (party, crm_deal, project, task,\n// hr_employee) with ONE broadcast per organization it touched, on the PRIVATE topic\n// `mx:entity-org:<organization>:<token>`, event `entity_bump`, payload\n// `{id, entity_type, organization_id, op, op_id, ids | null, at}` — ids, never values. It is written by\n// `platform.entity_org_notice_stmt` and admitted by `platform.entity_org_topic_admits` through the one RLS\n// policy on `realtime.messages`: a signed-in member of that organization (her own memberships, never the\n// admin route) whose role may read the table. The subscriber re-reads the ids through the read door, so\n// row security decides what she sees at read time.\n//\n// This file joins those topics through `@ai-matrx/realtime` — the ONE realtime layer, never a hand-written\n// `.channel(` — so the socket, `setAuth` before a private join, backoff, dedupe and wake backfill are the\n// manager's.\n//\n// · ONE CHANNEL PER (organization, token), HOWEVER MANY READERS — opened by the first, closed by the last.\n// · BURSTS COALESCED per reader: the first notice starts a `batchMs` timer, later ones join it, and\n// `maxWaitMs` caps how long a steady stream can hold a re-read back (knobs entity_data.realtime_batch_ms\n// / realtime_max_wait_ms).\n// · `ids: null` PASSES THROUGH — above knob entity_data.realtime_max_ids the database stops naming ids.\n// · ECHO DROPPED — a write that carried header `x-matrx-op-id` comes back with that `op_id`; a notice whose\n// op id this client minted (`@ai-matrx/records` `isOwnOp`) is our own write and is dropped.\n// · NO REPLAY ON A WAKE OR RECONNECT — the answer is `ids: null` (read again).\n\nimport {\n defineChannelNamespace,\n subscribeToRealtimeManager,\n type ChannelSpec,\n type RealtimeManager,\n} from \"@ai-matrx/realtime\";\nimport { isOwnOp } from \"@ai-matrx/records\";\n\nimport type { EntityClient } from \"./client\";\n\n/** The topic is a CONTRACT WITH THE DATABASE (a trigger writes it, an RLS policy parses it): built once, here. */\nexport const entityOrgChannel = defineChannelNamespace({\n namespace: \"entity-org\",\n foreignTopic: \"mx:entity-org\",\n parts: [\"organizationId\", \"token\"],\n description:\n \"One organization's changes to one platform type (contacts, deals, projects, tasks, employees). Carries NOTICES written by platform.entity_org_notice_stmt — which ids moved, never a value. Admitted by the one RLS policy on realtime.messages to members of that organization.\",\n});\n\n/** The broadcast event the database sends on an organization topic. */\nexport const ENTITY_NOTICE_EVENT = \"entity_bump\";\n\n/** Starting values of the knobs the runtime coalesces with (entity_data.realtime_batch_ms / _max_wait_ms). */\nexport const DEFAULT_BATCH_MS = 150;\nexport const DEFAULT_MAX_WAIT_MS = 1000;\n/** Starting value of knob entity_data.realtime_max_org_topics. */\nexport const DEFAULT_MAX_ORG_TOPICS = 20;\n\nexport interface EntityNotice {\n id?: string;\n entity_type?: string;\n organization_id?: string;\n op?: \"created\" | \"updated\" | \"deleted\";\n /** The client operation that caused it (header `x-matrx-op-id`), or null for any other writer. */\n op_id?: string | null;\n /** `null` MEANS \"more changed than the database lists: re-read\". */\n ids?: string[] | null;\n at?: string;\n}\n\n/** A redelivered notice is the same notice: the database mints one id per send. */\nexport function entityNoticeKey(notice: EntityNotice): string | undefined {\n return notice.id ?? (notice.at ? `${notice.organization_id ?? \"\"}:${notice.entity_type ?? \"\"}:${notice.at}` : undefined);\n}\n\nexport function entityOrgTopic(organizationId: string, token: string): string {\n return entityOrgChannel.topic({ organizationId, token });\n}\n\n/** The channel spec for one (organization, token): private, echo-guarded, deduped, backfilled. */\nexport function entityOrgChannelSpec(\n organizationId: string,\n token: string,\n onNotice: (notice: EntityNotice) => void,\n onBackfill: () => void,\n): ChannelSpec {\n return {\n topic: entityOrgTopic(organizationId, token),\n // Database Broadcast is authorized by RLS on realtime.messages; the manager awaits setAuth() first.\n private: true,\n // The sender is Postgres: no Matrx envelope. Our own write is known by the op id it carried.\n wire: { mode: \"raw\", isOwnMessage: (payload) => isOwnOp((payload as EntityNotice | null)?.op_id) },\n eventKey: (_source, payload) => entityNoticeKey((payload ?? {}) as EntityNotice),\n broadcast: [\n {\n event: ENTITY_NOTICE_EVENT,\n onMessage: (message) => {\n const notice = (message.data ?? {}) as EntityNotice;\n if (isOwnOp(notice.op_id)) return; // belt and braces: the manager's test ran first\n onNotice(notice);\n },\n },\n ],\n // Realtime has no replay: a channel back from a gap re-reads.\n onBackfill: () => onBackfill(),\n };\n}\n\n/**\n * Which organization topics to join, in order, until `max`: the active organization, the alias's `org`\n * filter, then the organizations of held rows by number of held rows (descending; organization id\n * ascending on ties), then the person's remaining memberships by id. Only memberships are kept when they\n * are known — any other topic is refused by the database anyway. Deterministic for the same input.\n */\nexport function orderOrganizationTopics(input: {\n active?: string | null;\n filter?: string | null;\n held?: ReadonlyMap<string, number>;\n members?: readonly string[] | null;\n max: number;\n}): string[] {\n const members = input.members ? new Set(input.members) : null;\n const out: string[] = [];\n const push = (org: string | null | undefined) => {\n if (!org || out.includes(org)) return;\n if (members && !members.has(org)) return;\n out.push(org);\n };\n push(input.active);\n push(input.filter);\n const held = [...(input.held ?? new Map<string, number>()).entries()].sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n for (const [org] of held) push(org);\n for (const org of [...(input.members ?? [])].sort()) push(org);\n return out.slice(0, Math.max(0, input.max));\n}\n\nexport interface EntityNoticeSink {\n /** `ids` to re-read, or `null` to re-read everything this reader shows. */\n rows(ids: string[] | null): void;\n}\n\nexport interface EntityRealtime {\n /** Hear `token` across `organizationIds`; the sink gets one coalesced call per burst. Returns the stop. */\n subscribe(target: { token: string; organizationIds: readonly string[] }, sink: EntityNoticeSink): () => void;\n}\n\nexport interface CreateEntityRealtimeOptions {\n /** The host's realtime manager. Left out, the AMBIENT manager the app mounted — never a socket of our own. */\n manager?: RealtimeManager;\n /** Knob entity_data.realtime_batch_ms (starting value 150). */\n batchMs?: number;\n /** Knob entity_data.realtime_max_wait_ms (starting value 1000). */\n maxWaitMs?: number;\n}\n\ninterface OrgListener {\n notice(notice: EntityNotice): void;\n backfill(): void;\n}\n\ninterface OrgChannel {\n listeners: Set<OrgListener>;\n stop: (() => void) | null;\n}\n\n/** The live-notice layer for platform lists, bound to a realtime manager. One per host is enough. */\nexport function createEntityRealtime(options: CreateEntityRealtimeOptions = {}): EntityRealtime {\n const batchMs = options.batchMs ?? DEFAULT_BATCH_MS;\n const maxWaitMs = options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;\n const channels = new Map<string, OrgChannel>();\n\n const open = (organizationId: string, token: string): OrgChannel => {\n const channel: OrgChannel = { listeners: new Set(), stop: null };\n const spec = entityOrgChannelSpec(\n organizationId,\n token,\n (notice) => {\n for (const listener of channel.listeners) listener.notice(notice);\n },\n () => {\n for (const listener of channel.listeners) listener.backfill();\n },\n );\n if (options.manager) {\n const handle = options.manager.open(spec);\n channel.stop = () => handle.close();\n } else {\n channel.stop = subscribeToRealtimeManager(() => spec);\n }\n return channel;\n };\n\n return {\n subscribe({ token, organizationIds }, sink) {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let firstAt = 0;\n let pending = new Set<string>();\n let wholeList = false;\n\n const flush = () => {\n if (timer !== null) clearTimeout(timer);\n timer = null;\n const ids = [...pending];\n const all = wholeList;\n pending = new Set();\n wholeList = false;\n // THE IDS ARE USED AS IDS. `null` costs a whole re-read; three ids re-read three rows.\n if (all) sink.rows(null);\n else if (ids.length > 0) sink.rows(ids);\n };\n const nudge = () => {\n const now = Date.now();\n if (timer === null) firstAt = now;\n else clearTimeout(timer);\n // Wait `batchMs` after the latest notice, never past `maxWaitMs` after the first one.\n const wait = Math.max(0, Math.min(batchMs, firstAt + maxWaitMs - now));\n timer = setTimeout(flush, wait);\n };\n\n const listener: OrgListener = {\n notice(notice) {\n if (notice.entity_type && notice.entity_type !== token) return;\n if (notice.ids == null) wholeList = true;\n else for (const id of notice.ids) pending.add(id);\n nudge();\n },\n backfill() {\n wholeList = true;\n flush();\n },\n };\n\n const keys = [...new Set(organizationIds)].map((org) => {\n const key = entityOrgTopic(org, token);\n const channel = channels.get(key) ?? open(org, token);\n channels.set(key, channel);\n channel.listeners.add(listener);\n return key;\n });\n\n return () => {\n if (timer !== null) clearTimeout(timer);\n for (const key of keys) {\n const channel = channels.get(key);\n if (!channel) continue;\n channel.listeners.delete(listener);\n if (channel.listeners.size > 0) continue;\n // The last reader of this (organization, token) left: its one subscription goes with it.\n channel.stop?.();\n channels.delete(key);\n }\n };\n },\n };\n}\n\n/** The three live-update knobs as the store answers them for an organization (starting values when unset). */\nexport async function readEntityRealtimeKnobs(\n client: Pick<EntityClient, \"knob\">,\n organizationId?: string | null,\n): Promise<{ batchMs: number; maxWaitMs: number; maxOrgTopics: number }> {\n const read = async (key: string, fallback: number) => {\n const answer = await client.knob({ key, organizationId: organizationId ?? null });\n const value = answer.ok ? Number(answer.data) : Number.NaN;\n if (!Number.isFinite(value)) {\n // SAID OUT LOUD: the starting value stands in for a knob the store did not answer.\n console.warn(`[@ai-matrx/entity-data] knob entity_data.${key} did not resolve; using its starting value ${fallback}.`);\n return fallback;\n }\n return value;\n };\n const [batchMs, maxWaitMs, maxOrgTopics] = await Promise.all([\n read(\"realtime_batch_ms\", DEFAULT_BATCH_MS),\n read(\"realtime_max_wait_ms\", DEFAULT_MAX_WAIT_MS),\n read(\"realtime_max_org_topics\", DEFAULT_MAX_ORG_TOPICS),\n ]);\n return { batchMs, maxWaitMs, maxOrgTopics };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACQA,IAAAA,eAAoC;;;ACDpC,kBAA2B;AAOpB,IAAM,kBAA8D;AAAA;AAAA,EAEzE,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AAAA;AAAA,EAEP,OAAO;AACT;AAEO,SAAS,eAAe,OAAgB,OAA6B;AAC1E,QAAM,aAAS,wBAAW,OAAO,KAAK;AACtC,QAAM,QAAQ,OAAO;AACrB,MAAI,SAAS,YAAY,KAAK,KAAK,GAAG;AACpC,UAAM,OAAO,gBAAgB,KAAK;AAClC,QAAI,MAAM;AAER,YAAM,SAAU,OAAO,QAA6C;AACpE,YAAM,QAAsB,EAAE,GAAG,QAAQ,MAAM,UAAU,WAAW,WAAW,kBAAkB,kBAAkB,KAAK;AACxH,aAAO,MAAM;AACb,aAAO,MAAM;AACb,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,QAAQ,MAAwB,SAAiB,MAAmD;AAClH,SAAO,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,SAAS,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC,EAAG,EAAE;AAC1E;;;ADvCO,IAAM,eAAe;AAQrB,IAAM,qBAAqB;AAqC3B,SAAS,mBAAmB,SAAkD;AACnF,QAAM,aAA4B,kCAAoB,QAAQ,cAAc,QAAQ,QAAQ;AAE5F,MAAI,mBAAmB;AASvB,WAAS,YAAY,SAA+B,SAAmD;AACrG,QAAI,CAAC,QAAS;AACd,UAAM,UAAU;AAChB,QAAI,OAAO,QAAQ,cAAc,YAAY;AAC3C,iBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,EAAG,SAAQ,UAAU,MAAM,KAAK;AAClF;AAAA,IACF;AACA,QAAI,CAAC,kBAAkB;AACrB,yBAAmB;AACnB,cAAQ;AAAA,QACN,4EAA4E,OAAO,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MAC7G;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,KACb,IACA,MACA,SAAiB,oBACjB,SAC2B;AAC3B,QAAI;AACF,YAAM,UAAU,OAAO,IAAI,IAAI,MAAM,EAAE,OAAO,CAAC;AAC/C,kBAAY,SAAS,OAAO;AAC5B,YAAM,SAAS,MAAM;AACrB,UAAI,OAAO,MAAO,QAAO,EAAE,IAAI,OAAO,OAAO,eAAe,OAAO,OAAO,GAAG,MAAM,IAAI,EAAE,EAAE,EAAE;AAC7F,aAAO,EAAE,IAAI,MAAM,MAAM,OAAO,KAAU;AAAA,IAC5C,SAAS,QAAQ;AAEf,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO,eAAe,EAAE,MAAM,IAAI,SAAS,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,EAAE,GAAG,GAAG,MAAM,IAAI,EAAE,EAAE;AAAA,MAC3H;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,KAAK,SAAS;AACZ,YAAM,OAAgC;AAAA,QACpC,eAAe,QAAQ;AAAA,QACvB,SAAS,QAAQ,SAAS;AAAA,QAC1B,UAAU,QAAQ,UAAU,CAAC;AAAA,QAC7B,UAAU,QAAQ,UAAU;AAAA,QAC5B,QAAQ,QAAQ,QAAQ;AAAA,QACxB,YAAY,QAAQ,YAAY;AAAA,QAChC,aAAa,QAAQ,YAAY;AAAA,QACjC,SAAS,QAAQ,SAAS;AAAA,QAC1B,cAAc,QAAQ,aAAa;AAAA,MACrC;AACA,aAAO,KAAuB,sBAAsB,IAAI;AAAA,IAC1D;AAAA,IACA,IAAI,SAAS;AACX,aAAO,KAAsB,cAAc,EAAE,eAAe,QAAQ,OAAO,OAAO,CAAC,GAAG,QAAQ,GAAG,EAAE,CAAC;AAAA,IACtG;AAAA,IACA,QAAQ,SAAS;AACf,aAAO,KAA0B,kBAAkB,EAAE,mBAAmB,QAAQ,kBAAkB,MAAM,SAAS,QAAQ,MAAM,CAAC;AAAA,IAClI;AAAA,IACA,OAAO,SAAS;AACd,aAAO;AAAA,QACL;AAAA,QACA,EAAE,eAAe,QAAQ,OAAO,mBAAmB,QAAQ,gBAAgB,QAAQ,QAAQ,KAAK,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE;AAAA,QACvH;AAAA,QACA,SAAS,QAAQ,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,IACA,OAAO,SAAS;AACd,aAAO;AAAA,QACL;AAAA,QACA,EAAE,eAAe,QAAQ,OAAO,MAAM,QAAQ,IAAI,gBAAgB,QAAQ,aAAa,SAAS,QAAQ,MAAM;AAAA,QAC9G;AAAA,QACA,SAAS,QAAQ,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,IACA,QAAQ,SAAS;AACf,aAAO,KAAwB,kBAAkB,EAAE,eAAe,QAAQ,OAAO,OAAO,CAAC,GAAG,QAAQ,GAAG,EAAE,GAAG,oBAAoB,SAAS,QAAQ,IAAI,CAAC;AAAA,IACxJ;AAAA,IACA,UAAU,SAAS;AACjB,aAAO,KAAwB,oBAAoB,EAAE,eAAe,QAAQ,OAAO,OAAO,CAAC,GAAG,QAAQ,GAAG,EAAE,GAAG,oBAAoB,SAAS,QAAQ,IAAI,CAAC;AAAA,IAC1J;AAAA,IACA,KAAK,SAAS;AACZ,aAAO,KAAc,gBAAgB,EAAE,WAAW,eAAe,OAAO,QAAQ,KAAK,mBAAmB,QAAQ,kBAAkB,KAAK,CAAC;AAAA,IAC1I;AAAA,IACA,MAAM,sBAAsB;AAC1B,YAAM,SAAS,MAAM,KAAc,WAAW,CAAC,GAAG,KAAK;AACvD,UAAI,CAAC,OAAO,GAAI,QAAO;AACvB,YAAM,OAAO,MAAM,QAAQ,OAAO,IAAI,IAAI,OAAO,OAAO,CAAC;AAEzD,YAAM,MAAM,KACT,IAAI,CAAC,QAAS,OAAO,QAAQ,WAAW,MAAM,OAAO,OAAO,QAAQ,WAAW,OAAO,OAAO,GAA8B,EAAE,CAAC,IAAI,IAAK,EACvI,OAAO,CAAC,OAAqB,OAAO,OAAO,QAAQ;AACtD,aAAO,EAAE,IAAI,MAAM,MAAM,IAAI;AAAA,IAC/B;AAAA,EACF;AACF;AAEA,SAAS,SAAS,MAA8D;AAC9E,SAAO,OAAO,EAAE,CAAC,YAAY,GAAG,KAAK,IAAI;AAC3C;;;AE/JA,IAAM,WAA6C;AAAA,EACjD,KAAK;AAAA,EACL,UAAU;AAAA,EACV,kBAAkB;AACpB;AAEO,IAAM,gBAAgB;AAMtB,SAAS,WAAW,QAAgB,YAAyC;AAClF,QAAM,WAAW,SAAS,MAAM;AAChC,MAAI,SAAU,QAAO;AACrB,MAAI,OAAO,WAAW,aAAa,EAAG,QAAO;AAC7C,SAAO,WAAW,IAAI,MAAM,IAAI,GAAG,aAAa,GAAG,MAAM,KAAK;AAChE;AAEA,SAAS,UAAU,QAAqB,YAAsD;AAC5F,QAAM,SAAS,WAAW,OAAO,QAAQ,UAAU;AACnD,QAAM,IAAI,OAAO;AACjB,UAAQ,OAAO,IAAI;AAAA,IACjB,KAAK;AAEH,aAAO,MAAM,QAAQ,MAAM,SAAY,EAAE,QAAQ,IAAI,QAAQ,IAAI,EAAE,QAAQ,IAAI,MAAM,OAAO,EAAE;AAAA,IAChG,KAAK;AACH,aAAO,MAAM,QAAQ,MAAM,SAAY,EAAE,QAAQ,IAAI,SAAS,SAAS,KAAK,IAAI,EAAE,QAAQ,IAAI,OAAO,OAAO,EAAE;AAAA,IAChH,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,QAAQ,IAAI,OAAO,IAAI,OAAO,EAAE;AAAA,IAC3C,KAAK;AACH,aAAO,EAAE,QAAQ,IAAI,MAAM,QAAQ,MAAM,QAAQ,CAAC,IAAI,IAAI,CAAC,EAAE;AAAA,IAC/D,KAAK;AACH,aAAO,MAAM,QAAQ,EAAE,QAAQ,IAAI,SAAS,SAAS,KAAK,IAAI,EAAE,QAAQ,IAAI,QAAQ;AAAA,IACtF,KAAK;AACH,aAAO,EAAE,QAAQ,IAAI,YAAY,OAAO,EAAE;AAAA,IAC5C;AAEE,aAAO,EAAE,QAAQ,IAAI,OAAQ,OAAuB,EAAE,GAAG,OAAO,EAAE;AAAA,EACtE;AACF;AAEO,SAAS,OAAO,OAA0B,aAAkC,oBAAI,IAAI,GAAoB;AAC7G,QAAM,IAAI,SAAS,CAAC;AACpB,QAAM,MAAuB,CAAC;AAC9B,QAAM,QAA+C,CAAC;AACtD,MAAI,EAAE,KAAM,OAAM,OAAO,EAAE;AAC3B,MAAI,EAAE,OAAO,EAAE,QAAQ,MAAO,OAAM,kBAAkB,EAAE;AACxD,MAAI,MAAM,QAAQ,MAAM,gBAAiB,KAAI,QAAQ;AACrD,MAAI,EAAE,SAAS,EAAE,MAAM,SAAS,EAAG,KAAI,SAAS,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,EAAE;AACpG,QAAM,SAAS,EAAE,QAAQ,KAAK;AAC9B,MAAI,OAAQ,KAAI,SAAS;AACzB,MAAI,EAAE,QAAQ,EAAE,KAAK,SAAS,GAAG;AAC/B,QAAI,OAAO,EAAE,KAAK,IAAoB,CAAC,OAAO;AAAA,MAC5C,QAAQ,WAAW,EAAE,QAAQ,UAAU;AAAA,MACvC,KAAK,EAAE,cAAc,SAAS,SAAS;AAAA,MACvC,OAAO;AAAA,IACT,EAAE;AAAA,EACJ;AACA,MAAI,OAAO,EAAE,aAAa,YAAY,OAAO,SAAS,EAAE,QAAQ,EAAG,KAAI,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE,QAAQ,CAAC;AACpH,SAAO;AACT;;;ACnDA,sBAKO;AACP,qBAAwB;AAKjB,IAAM,uBAAmB,wCAAuB;AAAA,EACrD,WAAW;AAAA,EACX,cAAc;AAAA,EACd,OAAO,CAAC,kBAAkB,OAAO;AAAA,EACjC,aACE;AACJ,CAAC;AAGM,IAAM,sBAAsB;AAG5B,IAAM,mBAAmB;AACzB,IAAM,sBAAsB;AAE5B,IAAM,yBAAyB;AAe/B,SAAS,gBAAgB,QAA0C;AACxE,SAAO,OAAO,OAAO,OAAO,KAAK,GAAG,OAAO,mBAAmB,EAAE,IAAI,OAAO,eAAe,EAAE,IAAI,OAAO,EAAE,KAAK;AAChH;AAEO,SAAS,eAAe,gBAAwB,OAAuB;AAC5E,SAAO,iBAAiB,MAAM,EAAE,gBAAgB,MAAM,CAAC;AACzD;AAGO,SAAS,qBACd,gBACA,OACA,UACA,YACa;AACb,SAAO;AAAA,IACL,OAAO,eAAe,gBAAgB,KAAK;AAAA;AAAA,IAE3C,SAAS;AAAA;AAAA,IAET,MAAM,EAAE,MAAM,OAAO,cAAc,CAAC,gBAAY,wBAAS,SAAiC,KAAK,EAAE;AAAA,IACjG,UAAU,CAAC,SAAS,YAAY,gBAAiB,WAAW,CAAC,CAAkB;AAAA,IAC/E,WAAW;AAAA,MACT;AAAA,QACE,OAAO;AAAA,QACP,WAAW,CAAC,YAAY;AACtB,gBAAM,SAAU,QAAQ,QAAQ,CAAC;AACjC,kBAAI,wBAAQ,OAAO,KAAK,EAAG;AAC3B,mBAAS,MAAM;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAEA,YAAY,MAAM,WAAW;AAAA,EAC/B;AACF;AAQO,SAAS,wBAAwB,OAM3B;AACX,QAAM,UAAU,MAAM,UAAU,IAAI,IAAI,MAAM,OAAO,IAAI;AACzD,QAAM,MAAgB,CAAC;AACvB,QAAM,OAAO,CAAC,QAAmC;AAC/C,QAAI,CAAC,OAAO,IAAI,SAAS,GAAG,EAAG;AAC/B,QAAI,WAAW,CAAC,QAAQ,IAAI,GAAG,EAAG;AAClC,QAAI,KAAK,GAAG;AAAA,EACd;AACA,OAAK,MAAM,MAAM;AACjB,OAAK,MAAM,MAAM;AACjB,QAAM,OAAO,CAAC,IAAI,MAAM,QAAQ,oBAAI,IAAoB,GAAG,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE;AAC5I,aAAW,CAAC,GAAG,KAAK,KAAM,MAAK,GAAG;AAClC,aAAW,OAAO,CAAC,GAAI,MAAM,WAAW,CAAC,CAAE,EAAE,KAAK,EAAG,MAAK,GAAG;AAC7D,SAAO,IAAI,MAAM,GAAG,KAAK,IAAI,GAAG,MAAM,GAAG,CAAC;AAC5C;AAgCO,SAAS,qBAAqB,UAAuC,CAAC,GAAmB;AAC9F,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,WAAW,oBAAI,IAAwB;AAE7C,QAAM,OAAO,CAAC,gBAAwB,UAA8B;AAClE,UAAM,UAAsB,EAAE,WAAW,oBAAI,IAAI,GAAG,MAAM,KAAK;AAC/D,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA,CAAC,WAAW;AACV,mBAAW,YAAY,QAAQ,UAAW,UAAS,OAAO,MAAM;AAAA,MAClE;AAAA,MACA,MAAM;AACJ,mBAAW,YAAY,QAAQ,UAAW,UAAS,SAAS;AAAA,MAC9D;AAAA,IACF;AACA,QAAI,QAAQ,SAAS;AACnB,YAAM,SAAS,QAAQ,QAAQ,KAAK,IAAI;AACxC,cAAQ,OAAO,MAAM,OAAO,MAAM;AAAA,IACpC,OAAO;AACL,cAAQ,WAAO,4CAA2B,MAAM,IAAI;AAAA,IACtD;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,UAAU,EAAE,OAAO,gBAAgB,GAAG,MAAM;AAC1C,UAAI,QAA8C;AAClD,UAAI,UAAU;AACd,UAAI,UAAU,oBAAI,IAAY;AAC9B,UAAI,YAAY;AAEhB,YAAM,QAAQ,MAAM;AAClB,YAAI,UAAU,KAAM,cAAa,KAAK;AACtC,gBAAQ;AACR,cAAM,MAAM,CAAC,GAAG,OAAO;AACvB,cAAM,MAAM;AACZ,kBAAU,oBAAI,IAAI;AAClB,oBAAY;AAEZ,YAAI,IAAK,MAAK,KAAK,IAAI;AAAA,iBACd,IAAI,SAAS,EAAG,MAAK,KAAK,GAAG;AAAA,MACxC;AACA,YAAM,QAAQ,MAAM;AAClB,cAAM,MAAM,KAAK,IAAI;AACrB,YAAI,UAAU,KAAM,WAAU;AAAA,YACzB,cAAa,KAAK;AAEvB,cAAM,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,UAAU,YAAY,GAAG,CAAC;AACrE,gBAAQ,WAAW,OAAO,IAAI;AAAA,MAChC;AAEA,YAAM,WAAwB;AAAA,QAC5B,OAAO,QAAQ;AACb,cAAI,OAAO,eAAe,OAAO,gBAAgB,MAAO;AACxD,cAAI,OAAO,OAAO,KAAM,aAAY;AAAA,cAC/B,YAAW,MAAM,OAAO,IAAK,SAAQ,IAAI,EAAE;AAChD,gBAAM;AAAA,QACR;AAAA,QACA,WAAW;AACT,sBAAY;AACZ,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,YAAM,OAAO,CAAC,GAAG,IAAI,IAAI,eAAe,CAAC,EAAE,IAAI,CAAC,QAAQ;AACtD,cAAM,MAAM,eAAe,KAAK,KAAK;AACrC,cAAM,UAAU,SAAS,IAAI,GAAG,KAAK,KAAK,KAAK,KAAK;AACpD,iBAAS,IAAI,KAAK,OAAO;AACzB,gBAAQ,UAAU,IAAI,QAAQ;AAC9B,eAAO;AAAA,MACT,CAAC;AAED,aAAO,MAAM;AACX,YAAI,UAAU,KAAM,cAAa,KAAK;AACtC,mBAAW,OAAO,MAAM;AACtB,gBAAM,UAAU,SAAS,IAAI,GAAG;AAChC,cAAI,CAAC,QAAS;AACd,kBAAQ,UAAU,OAAO,QAAQ;AACjC,cAAI,QAAQ,UAAU,OAAO,EAAG;AAEhC,kBAAQ,OAAO;AACf,mBAAS,OAAO,GAAG;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAGA,eAAsB,wBACpB,QACA,gBACuE;AACvE,QAAM,OAAO,OAAO,KAAa,aAAqB;AACpD,UAAM,SAAS,MAAM,OAAO,KAAK,EAAE,KAAK,gBAAgB,kBAAkB,KAAK,CAAC;AAChF,UAAM,QAAQ,OAAO,KAAK,OAAO,OAAO,IAAI,IAAI,OAAO;AACvD,QAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAE3B,cAAQ,KAAK,4CAA4C,GAAG,8CAA8C,QAAQ,GAAG;AACrH,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACA,QAAM,CAAC,SAAS,WAAW,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC3D,KAAK,qBAAqB,gBAAgB;AAAA,IAC1C,KAAK,wBAAwB,mBAAmB;AAAA,IAChD,KAAK,2BAA2B,sBAAsB;AAAA,EACxD,CAAC;AACD,SAAO,EAAE,SAAS,WAAW,aAAa;AAC5C;","names":["import_core"]}
1
+ {"version":3,"sources":["../../src/core/index.ts"],"sourcesContent":["// @ai-matrx/entity-data — DEPRECATED: this package now lives inside records as `@ai-matrx/records/entity`.\n// A thin re-export until no published @ai-matrx package imports it (check-tarball-sibling-imports).\nexport * from \"@ai-matrx/records/entity\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAAA;AAEA,yBAAc,qCAFd;","names":[]}
@@ -1,27 +1 @@
1
- import { p as EntityWireQuery } from '../realtime-DL-bfUFj.cjs';
2
- export { C as CreateEntityClientOptions, a as CreateEntityRealtimeOptions, D as DEFAULT_BATCH_MS, b as DEFAULT_MAX_ORG_TOPICS, c as DEFAULT_MAX_WAIT_MS, E as ENTITY_DOOR_SCHEMA, d as ENTITY_NOTICE_EVENT, e as EntityClient, f as EntityColumnWire, g as EntityColumnsAnswer, h as EntityGetAnswer, i as EntityListAnswer, j as EntityListRequest, k as EntityNotice, l as EntityNoticeSink, m as EntityRealtime, n as EntitySortApplied, o as EntityWirePredicate, q as EntityWireRef, r as EntityWireRow, s as EntityWireSort, t as EntityWriteAnswer, u as EntityWriteOptions, O as OP_ID_HEADER, v as createEntityClient, w as createEntityRealtime, x as entityNoticeKey, y as entityOrgChannel, z as entityOrgChannelSpec, A as entityOrgTopic, B as orderOrganizationTopics, F as readEntityRealtimeKnobs } from '../realtime-DL-bfUFj.cjs';
3
- import { RecordsErrorCode, PgError, RecordsError } from '@ai-matrx/records';
4
- import { Query } from '@ai-matrx/records/data';
5
- import '@ai-matrx/realtime';
6
-
7
- /**
8
- * Every MX code a read door raises, and the engine code a screen branches on. A code the doors learn
9
- * later and this map lacks comes back `internal` with `unmapped: true` (never silently "handled").
10
- */
11
- declare const ENTITY_REFUSALS: Readonly<Record<string, RecordsErrorCode>>;
12
- declare function mapEntityError(error: PgError, where: string): RecordsError;
13
- /** A refusal this package itself raises (an alias nobody declared, a write not offered yet). */
14
- declare function refusal(code: RecordsErrorCode, message: string, hint?: string): {
15
- ok: false;
16
- error: RecordsError;
17
- };
18
-
19
- declare const CUSTOM_PREFIX = "_custom.";
20
- /**
21
- * Applets column key → the door's identifier. A custom field is `_custom.<key>` (the key as
22
- * `columns()` answered it — the same plain key for every viewer; `<key>@<organization_id>` is accepted, never produced).
23
- */
24
- declare function wireColumn(column: string, customKeys: ReadonlySet<string>): string;
25
- declare function toWire(query: Query | undefined, customKeys?: ReadonlySet<string>): EntityWireQuery;
26
-
27
- export { CUSTOM_PREFIX, ENTITY_REFUSALS, EntityWireQuery, mapEntityError, refusal, toWire, wireColumn };
1
+ export * from '@ai-matrx/records/entity';
@@ -1,27 +1 @@
1
- import { p as EntityWireQuery } from '../realtime-DL-bfUFj.js';
2
- export { C as CreateEntityClientOptions, a as CreateEntityRealtimeOptions, D as DEFAULT_BATCH_MS, b as DEFAULT_MAX_ORG_TOPICS, c as DEFAULT_MAX_WAIT_MS, E as ENTITY_DOOR_SCHEMA, d as ENTITY_NOTICE_EVENT, e as EntityClient, f as EntityColumnWire, g as EntityColumnsAnswer, h as EntityGetAnswer, i as EntityListAnswer, j as EntityListRequest, k as EntityNotice, l as EntityNoticeSink, m as EntityRealtime, n as EntitySortApplied, o as EntityWirePredicate, q as EntityWireRef, r as EntityWireRow, s as EntityWireSort, t as EntityWriteAnswer, u as EntityWriteOptions, O as OP_ID_HEADER, v as createEntityClient, w as createEntityRealtime, x as entityNoticeKey, y as entityOrgChannel, z as entityOrgChannelSpec, A as entityOrgTopic, B as orderOrganizationTopics, F as readEntityRealtimeKnobs } from '../realtime-DL-bfUFj.js';
3
- import { RecordsErrorCode, PgError, RecordsError } from '@ai-matrx/records';
4
- import { Query } from '@ai-matrx/records/data';
5
- import '@ai-matrx/realtime';
6
-
7
- /**
8
- * Every MX code a read door raises, and the engine code a screen branches on. A code the doors learn
9
- * later and this map lacks comes back `internal` with `unmapped: true` (never silently "handled").
10
- */
11
- declare const ENTITY_REFUSALS: Readonly<Record<string, RecordsErrorCode>>;
12
- declare function mapEntityError(error: PgError, where: string): RecordsError;
13
- /** A refusal this package itself raises (an alias nobody declared, a write not offered yet). */
14
- declare function refusal(code: RecordsErrorCode, message: string, hint?: string): {
15
- ok: false;
16
- error: RecordsError;
17
- };
18
-
19
- declare const CUSTOM_PREFIX = "_custom.";
20
- /**
21
- * Applets column key → the door's identifier. A custom field is `_custom.<key>` (the key as
22
- * `columns()` answered it — the same plain key for every viewer; `<key>@<organization_id>` is accepted, never produced).
23
- */
24
- declare function wireColumn(column: string, customKeys: ReadonlySet<string>): string;
25
- declare function toWire(query: Query | undefined, customKeys?: ReadonlySet<string>): EntityWireQuery;
26
-
27
- export { CUSTOM_PREFIX, ENTITY_REFUSALS, EntityWireQuery, mapEntityError, refusal, toWire, wireColumn };
1
+ export * from '@ai-matrx/records/entity';