@ai-matrx/records 0.57.0 → 0.57.2

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.
@@ -2243,6 +2243,7 @@ __export(react_exports, {
2243
2243
  VALUE_ALTERNATE_KEYS: () => VALUE_ALTERNATE_KEYS,
2244
2244
  VALUE_ENVELOPE_KEYS: () => VALUE_ENVELOPE_KEYS,
2245
2245
  actionRefusals: () => actionRefusals,
2246
+ allowsOtherValues: () => allowsOtherValues,
2246
2247
  archivedByLine: () => archivedByLine,
2247
2248
  archivedWhen: () => archivedWhen,
2248
2249
  asPermissionLevel: () => asPermissionLevel,
@@ -2303,6 +2304,7 @@ __export(react_exports, {
2303
2304
  useMyLevels: () => useMyLevels,
2304
2305
  useOptionalRecordsClient: () => useOptionalRecordsClient,
2305
2306
  useRecord: () => useRecord,
2307
+ useRecordChangeRevision: () => useRecordChangeRevision,
2306
2308
  useRecordMutation: () => useRecordMutation,
2307
2309
  useRecords: () => useRecords,
2308
2310
  useRecordsActor: () => useRecordsActor,
@@ -3161,6 +3163,7 @@ var STORE_DOORS = [
3161
3163
  { name: "table_declare", args: "p_organization_id uuid, p_spec jsonb", returns: "uuid", security: "definer" },
3162
3164
  { name: "table_decorate", args: "p_organization_id uuid, p_table_id uuid, p_path text[], p_value jsonb DEFAULT NULL::jsonb", returns: "jsonb", security: "definer" },
3163
3165
  { name: "table_decorations", args: "p_organization_id uuid, p_table_id uuid", returns: "jsonb", security: "definer" },
3166
+ { name: "table_facts", args: "p_organization_id uuid", returns: "TABLE(table_id uuid, visibility text, mine boolean)", security: "definer" },
3164
3167
  { name: "table_from_example", args: "p_organization_id uuid, p_home_id uuid, p_example jsonb", returns: "jsonb", security: "definer" },
3165
3168
  { name: "table_has_a_visible_record", args: "p_user uuid, p_organization_id uuid, p_table_id uuid", returns: "boolean", security: "definer" },
3166
3169
  { name: "table_is_live", args: "p_organization_id uuid, p_table_id uuid", returns: "boolean", security: "invoker" },
@@ -4656,6 +4659,7 @@ var DOORS = {
4656
4659
  // everywhere in that organization. This is the one question the rail has to
4657
4660
  // ask, under the same wall and visibility gate as `portals` itself.
4658
4661
  portalTables: "portal_tables",
4662
+ tableFacts: "table_facts",
4659
4663
  portalCard: "portal_card",
4660
4664
  portalDeclare: "portal_declare",
4661
4665
  portalInvite: "portal_invite",
@@ -5032,6 +5036,22 @@ function staleWriteDetail(error) {
5032
5036
  };
5033
5037
  }
5034
5038
 
5039
+ // src/core/recordChanges.ts
5040
+ var listeners = /* @__PURE__ */ new Set();
5041
+ function announceRecordChanged(recordId) {
5042
+ for (const listener of [...listeners]) {
5043
+ try {
5044
+ listener(recordId);
5045
+ } catch (thrown) {
5046
+ console.error("[@ai-matrx/records] a record-change listener threw", thrown);
5047
+ }
5048
+ }
5049
+ }
5050
+ function onRecordChanged(listener) {
5051
+ listeners.add(listener);
5052
+ return () => listeners.delete(listener);
5053
+ }
5054
+
5035
5055
  // src/core/query.ts
5036
5056
  var DEFAULT_PAGE_SIZE = 50;
5037
5057
  function planQuery(query) {
@@ -5073,12 +5093,22 @@ function intersectIds(sets) {
5073
5093
 
5074
5094
  // src/core/client.ts
5075
5095
  var DEFAULT_SCHEMA = "custom";
5096
+ var A_PERSONS_REFUSAL = /* @__PURE__ */ new Set([
5097
+ "refused_by_rule",
5098
+ "stale_write",
5099
+ "invalid_argument",
5100
+ "already_exists",
5101
+ "missing_reference"
5102
+ ]);
5103
+ function isAPersonsRefusal(code) {
5104
+ return A_PERSONS_REFUSAL.has(code);
5105
+ }
5076
5106
  function sink(config) {
5077
- return config.onError ?? ((e) => (
5078
- // The defined degradation, stated: loud, with the store's own words, and
5079
- // naming where it happened. Never silence.
5080
- console.error(`[@ai-matrx/records] ${e.where}: ${e.code} \u2014 ${e.message}${e.hint ? ` (${e.hint})` : ""}`)
5081
- ));
5107
+ return config.onError ?? ((e) => {
5108
+ const line = `[@ai-matrx/records] ${e.where}: ${e.code} \u2014 ${e.message}${e.hint ? ` (${e.hint})` : ""}`;
5109
+ if (isAPersonsRefusal(e.code)) console.info(line);
5110
+ else console.error(line);
5111
+ });
5082
5112
  }
5083
5113
  function createRecordsClient(config) {
5084
5114
  const schema = config.schema ?? DEFAULT_SCHEMA;
@@ -5185,21 +5215,26 @@ function createRecordsClient(config) {
5185
5215
  },
5186
5216
  "recordUpdate"
5187
5217
  );
5218
+ if (result.ok) announceRecordChanged(record_id);
5188
5219
  return result;
5189
5220
  },
5190
5221
  async recordDelete({ record_id }) {
5191
- return callDoor(
5222
+ const result = await callDoor(
5192
5223
  DOORS.recordDelete,
5193
5224
  { p_organization_id: org, p_record_id: record_id },
5194
5225
  "recordDelete"
5195
5226
  );
5227
+ if (result.ok) announceRecordChanged(record_id);
5228
+ return result;
5196
5229
  },
5197
5230
  async recordRestore({ record_id }) {
5198
- return callDoor(
5231
+ const result = await callDoor(
5199
5232
  DOORS.recordRestore,
5200
5233
  { p_organization_id: org, p_record_id: record_id },
5201
5234
  "recordRestore"
5202
5235
  );
5236
+ if (result.ok) announceRecordChanged(record_id);
5237
+ return result;
5203
5238
  },
5204
5239
  async tableArchive({ table_id, chunk, includeTable }) {
5205
5240
  return callDoor(
@@ -5249,6 +5284,24 @@ function createRecordsClient(config) {
5249
5284
  })
5250
5285
  );
5251
5286
  },
5287
+ async tableArchived({ table_id }) {
5288
+ const kernel = await callDoor(DOORS.tableKernelId, {}, "tableArchived.kernel");
5289
+ if (!kernel.ok) return err(kernel.error);
5290
+ const PAGE = 200;
5291
+ for (let page = 0; page < 25; page += 1) {
5292
+ const answered = await this.listArchived({
5293
+ table_id: kernel.data,
5294
+ lane: "org",
5295
+ limit: PAGE,
5296
+ offset: page * PAGE
5297
+ });
5298
+ if (!answered.ok) return err(answered.error);
5299
+ const hit = answered.data.rows.find((row) => row.id === table_id);
5300
+ if (hit) return ok(hit);
5301
+ if (answered.data.total !== null) return ok(null);
5302
+ }
5303
+ return ok(null);
5304
+ },
5252
5305
  async listArchived({ table_id, lane = DEFAULT_ARCHIVE_LANE, limit = 50, offset = 0 }) {
5253
5306
  const door = await callDoor(
5254
5307
  DOORS.readRecordsArchived,
@@ -5414,6 +5467,23 @@ function createRecordsClient(config) {
5414
5467
  const { document } = unfold(row.document);
5415
5468
  return { ...document, id: row.id, organization_id: org };
5416
5469
  });
5470
+ if (doorExists(DOORS.tableFacts)) {
5471
+ const facts = await callDoor(
5472
+ DOORS.tableFacts,
5473
+ { p_organization_id: org },
5474
+ "tableList.facts"
5475
+ );
5476
+ if (facts.ok) {
5477
+ const byId = new Map((facts.data ?? []).map((f) => [f.table_id, f]));
5478
+ const me = config.actor.user_id ?? null;
5479
+ for (const table of tables) {
5480
+ const fact = byId.get(table.id);
5481
+ if (!fact) continue;
5482
+ table.visibility = fact.visibility;
5483
+ if (fact.mine && me) table.created_by = me;
5484
+ }
5485
+ }
5486
+ }
5417
5487
  tables.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
5418
5488
  return ok(tables);
5419
5489
  },
@@ -5450,9 +5520,11 @@ function createRecordsClient(config) {
5450
5520
  );
5451
5521
  },
5452
5522
  async fieldDeclare({ table_id, spec }) {
5523
+ const { allow_other, ...rest } = spec;
5524
+ const p_spec = allow_other === void 0 ? rest : { ...rest, config: { ...rest.config ?? {}, allow_other } };
5453
5525
  return callDoor(
5454
5526
  DOORS.fieldDeclare,
5455
- { p_organization_id: org, p_table_id: table_id, p_spec: spec },
5527
+ { p_organization_id: org, p_table_id: table_id, p_spec },
5456
5528
  "fieldDeclare"
5457
5529
  );
5458
5530
  },
@@ -6420,14 +6492,16 @@ function createRecordsClient(config) {
6420
6492
  );
6421
6493
  },
6422
6494
  async restoreVersion({ record_id, version }) {
6423
- return callDoor(
6495
+ const result = await callDoor(
6424
6496
  DOORS.restoreVersion,
6425
6497
  { p_organization_id: org, p_record_id: record_id, p_version: version },
6426
6498
  "restoreVersion"
6427
6499
  );
6500
+ if (result.ok) announceRecordChanged(record_id);
6501
+ return result;
6428
6502
  },
6429
6503
  async valueRestore({ record_id, field_key, version }) {
6430
- return callDoor(
6504
+ const answered = await callDoor(
6431
6505
  DOORS.valueRestore,
6432
6506
  {
6433
6507
  p_organization_id: org,
@@ -6437,6 +6511,8 @@ function createRecordsClient(config) {
6437
6511
  },
6438
6512
  "valueRestore"
6439
6513
  );
6514
+ if (answered.ok) announceRecordChanged(record_id);
6515
+ return answered;
6440
6516
  },
6441
6517
  async commentThread({ record_id, include_resolved }) {
6442
6518
  return callDoor(
@@ -7363,13 +7439,24 @@ function useRecords(tableId, options = {}) {
7363
7439
  const liveReason = realtime ? tableId ? "Live: changes to this table re-read through the read door." : "Not live: no table selected." : "Not live: this host bound no realtime port, so this list updates when you reload it.";
7364
7440
  return { ...base, live, liveReason };
7365
7441
  }
7442
+ function useRecordChangeRevision(recordId) {
7443
+ const [revision, setRevision] = (0, import_react2.useState)(0);
7444
+ (0, import_react2.useEffect)(() => {
7445
+ if (!recordId) return;
7446
+ return onRecordChanged((changed) => {
7447
+ if (changed === recordId) setRevision((n) => n + 1);
7448
+ });
7449
+ }, [recordId]);
7450
+ return revision;
7451
+ }
7366
7452
  function useRecord(recordId, withComputed = false) {
7453
+ const changed = useRecordChangeRevision(recordId);
7367
7454
  return useAsync(
7368
7455
  async (client) => {
7369
7456
  if (!recordId) return { ok: true, data: null };
7370
7457
  return client.recordRead({ record_id: recordId, withComputed });
7371
7458
  },
7372
- [recordId, withComputed]
7459
+ [recordId, withComputed, changed]
7373
7460
  );
7374
7461
  }
7375
7462
  function useRecordMutation() {
@@ -7715,6 +7802,11 @@ var OVERRIDE_POLICIES = [
7715
7802
  var FRESHNESS = ["live", "cached", "snapshot"];
7716
7803
  var TABLE_DISPLAYS = ["list", "grid", "board", "calendar", "timeline"];
7717
7804
 
7805
+ // src/field.ts
7806
+ function allowsOtherValues(field) {
7807
+ return field.type === "list" && field.config?.["allow_other"] === true;
7808
+ }
7809
+
7718
7810
  // src/choice.ts
7719
7811
  var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7720
7812
  function optionKey(option) {