@ai-matrx/records 0.26.0 → 0.28.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
@@ -1,5 +1,56 @@
1
1
  # Changelog — @ai-matrx/records
2
2
 
3
+ ## 0.28.0
4
+
5
+ **The owner's side of a booking page — the one method that was standing between
6
+ `BookingSlots` and being the panel it should be.**
7
+
8
+ `bookings({ table_id? })` over `custom.bookings`, which has been live and granted to
9
+ `authenticated` since lane BOOKING landed and which no client could name. It is
10
+ modelled line for line on `forms()`, because a booking page IS an `anon_form`: the
11
+ same object, asked for its booking half, opened and closed by the same
12
+ `anonPublish`. `BookingSummary` (`src/rule.ts`) carries what makes the list worth
13
+ opening — booked, cancelled, still upcoming, how many slots somebody is holding
14
+ right now, and when the next appointment is — every number the store's own, because
15
+ a screen that counted appointments in the browser would drift from the unique index
16
+ that actually protects a slot.
17
+
18
+ `bookingPath(formId)` sits beside `publicFormPath` for the same reason: the page id
19
+ IS the capability, and the visitor's `booking_ref` is the separate secret that lets
20
+ ONE person manage the appointment they made.
21
+
22
+ The VISITOR's three doors (`booking_public`, `booking_hold`, `booking_confirm`) are
23
+ deliberately still absent from this client. They are server-lane, a signed-out
24
+ visitor reaches them through the host's own route, and a browser never calls them.
25
+
26
+ ## 0.27.0
27
+
28
+ **"Give me a board of deals by stage" — the rules that judge every move become
29
+ something a screen can ask about before the finger lets go.**
30
+
31
+ A pipeline is not a new kind of thing and this release adds none. It is a Choice
32
+ Field a Table names as its stage field, plus transition Rules of the ONE Rule
33
+ object — so a move is an ordinary record write the store judges, History records
34
+ because it IS a write, and every surface already knows how to read.
35
+
36
+ `src/pipeline.ts`: `Pipeline`, `PipelineStage`, `PipelineRule`, `PipelineColumn`,
37
+ `PipelineVerdict`, `PipelineMissingField`, `PipelineDeclared`, `PipelineSpec`.
38
+
39
+ Six client methods, each one door: `pipelineRead`, `pipelineBoard`,
40
+ `pipelineTransitionRefusal`, `pipelineMove`, `pipelineDeclare`, `tableStageField`.
41
+
42
+ THIS PACKAGE ENFORCES NOTHING, and that is the point. `pipelineTransitionRefusal`
43
+ asks the store the same question the write will ask — the same Rules, through the
44
+ same evaluator, on the document the move WOULD produce — and hands back the store's
45
+ own sentence plus, when a stage demands columns that are empty, exactly which ones.
46
+ A board that decided in the browser would be a second opinion with nothing keeping
47
+ it equal to the first, and it would be the comfortable one, because it is the one
48
+ nobody can be refused by.
49
+
50
+ `mayDrag(level)` is the one thing this package DOES decide, and it is about the
51
+ person, not the rules: a viewer's card carries no drag handle at all rather than
52
+ one that looks live and snaps back every time.
53
+
3
54
  ## 0.26.0
4
55
 
5
56
  **"Text me on a new lead; email me a Monday summary" — the second half of that
@@ -53,6 +53,7 @@ __export(core_exports, {
53
53
  PARITY_FIELD_TYPES: () => PARITY_FIELD_TYPES,
54
54
  PERMISSION_LEVELS: () => PERMISSION_LEVELS,
55
55
  PER_VALUE_ACCESS_WORDS: () => PER_VALUE_ACCESS_WORDS,
56
+ PIPELINE_RULE_KINDS: () => PIPELINE_RULE_KINDS,
56
57
  REFUSALS_WORTH_REPEATING: () => REFUSALS_WORTH_REPEATING,
57
58
  RELATION_BINDINGS: () => RELATION_BINDINGS,
58
59
  RELATION_CARDINALITIES: () => RELATION_CARDINALITIES,
@@ -72,6 +73,7 @@ __export(core_exports, {
72
73
  asPermissionLevel: () => asPermissionLevel,
73
74
  asWriteConflict: () => asWriteConflict,
74
75
  atLeast: () => atLeast,
76
+ bookingPath: () => bookingPath,
75
77
  cellText: () => cellText,
76
78
  choiceSlug: () => choiceSlug,
77
79
  choiceValuesOf: () => choiceValuesOf,
@@ -94,6 +96,7 @@ __export(core_exports, {
94
96
  isTheChosen: () => isTheChosen,
95
97
  looksLikeAnId: () => looksLikeAnId,
96
98
  mapPgError: () => mapPgError,
99
+ mayDrag: () => mayDrag,
97
100
  measureKey: () => measureKey,
98
101
  memoryStore: () => memoryStore,
99
102
  mintClientKey: () => mintClientKey,
@@ -242,7 +245,16 @@ function looksLikeAnId(value) {
242
245
  return typeof value === "string" && UUID.test(value);
243
246
  }
244
247
 
248
+ // src/pipeline.ts
249
+ var PIPELINE_RULE_KINDS = ["moves", "requires", "who", "limit", "entry"];
250
+ function mayDrag(level) {
251
+ return level === "editor" || level === "admin" || level === "owner";
252
+ }
253
+
245
254
  // src/rule.ts
255
+ function bookingPath(formId) {
256
+ return `/b/${formId}`;
257
+ }
246
258
  function publicFormPath(formId) {
247
259
  return `/f/${formId}`;
248
260
  }
@@ -2354,6 +2366,12 @@ var DOORS = {
2354
2366
  tableKernelId: "table_kernel_id",
2355
2367
  // A saved view references its membership Rule, and the STORE decides who is
2356
2368
  // in it — the screen never re-implements the predicate.
2369
+ pipelineDeclare: "pipeline_declare",
2370
+ pipelineRead: "pipeline_read",
2371
+ pipelineBoard: "pipeline_board",
2372
+ pipelineTransitionRefusal: "pipeline_transition_refusal",
2373
+ pipelineMove: "pipeline_move",
2374
+ tableStageField: "table_stage_field",
2357
2375
  ruleMembers: "rule_members",
2358
2376
  ruleMembership: "rule_membership",
2359
2377
  ruleRun: "rule_run",
@@ -2369,6 +2387,12 @@ var DOORS = {
2369
2387
  // are server-lane doors and a browser never calls them.
2370
2388
  forms: "forms",
2371
2389
  formDeclare: "form_declare",
2390
+ // PRODUCTS row 14 — "let clients book a 30-minute consult". A booking page IS an
2391
+ // `anon_form`, so it is opened and closed by `anonPublish` like any other; this is
2392
+ // the owner's list of them with the numbers that make it worth opening. The
2393
+ // VISITOR's three (`booking_public`, `booking_hold`, `booking_confirm`) are
2394
+ // deliberately absent: they are server-lane and a browser never calls them.
2395
+ bookings: "bookings",
2372
2396
  // PRODUCTS row 9 — "pull my spreadsheet in". W4-IO built `io_import_open` and
2373
2397
  // `io_import_rows`; lane IMPORT gave a run an IDENTITY (the file's sha-256, so
2374
2398
  // a re-import doubles nothing), a duplicate key, a policy, per-row outcomes
@@ -3153,6 +3177,54 @@ function createRecordsClient(config) {
3153
3177
  async ruleKernelId() {
3154
3178
  return callDoor(DOORS.ruleKernelId, {}, "ruleKernelId");
3155
3179
  },
3180
+ async pipelineRead({ table_id }) {
3181
+ return callDoor(
3182
+ DOORS.pipelineRead,
3183
+ { p_organization_id: org, p_table_id: table_id },
3184
+ "pipelineRead"
3185
+ );
3186
+ },
3187
+ async pipelineBoard({ table_id, measure }) {
3188
+ return callDoor(
3189
+ DOORS.pipelineBoard,
3190
+ { p_organization_id: org, p_table_id: table_id, p_measure: measure ?? null },
3191
+ "pipelineBoard"
3192
+ );
3193
+ },
3194
+ async pipelineTransitionRefusal({ record_id, to }) {
3195
+ return callDoor(
3196
+ DOORS.pipelineTransitionRefusal,
3197
+ { p_organization_id: org, p_record_id: record_id, p_to: to },
3198
+ "pipelineTransitionRefusal"
3199
+ );
3200
+ },
3201
+ async pipelineMove({ record_id, to, also, expected_version }) {
3202
+ return callDoor(
3203
+ DOORS.pipelineMove,
3204
+ {
3205
+ p_organization_id: org,
3206
+ p_record_id: record_id,
3207
+ p_to: to,
3208
+ p_also: also ?? {},
3209
+ p_expected_version: expected_version ?? null
3210
+ },
3211
+ "pipelineMove"
3212
+ );
3213
+ },
3214
+ async pipelineDeclare({ table_id, spec }) {
3215
+ return callDoor(
3216
+ DOORS.pipelineDeclare,
3217
+ { p_organization_id: org, p_table_id: table_id, p_spec: spec },
3218
+ "pipelineDeclare"
3219
+ );
3220
+ },
3221
+ async tableStageField({ table_id }) {
3222
+ return callDoor(
3223
+ DOORS.tableStageField,
3224
+ { p_organization_id: org, p_table_id: table_id },
3225
+ "tableStageField"
3226
+ );
3227
+ },
3156
3228
  async ruleMembers({ rule_id }) {
3157
3229
  return callDoor(
3158
3230
  DOORS.ruleMembers,
@@ -3174,6 +3246,13 @@ function createRecordsClient(config) {
3174
3246
  "forms"
3175
3247
  );
3176
3248
  },
3249
+ async bookings(args) {
3250
+ return callDoor(
3251
+ DOORS.bookings,
3252
+ { p_organization_id: org, p_table_id: args?.table_id ?? null },
3253
+ "bookings"
3254
+ );
3255
+ },
3177
3256
  async formDeclare(args) {
3178
3257
  return callDoor(
3179
3258
  DOORS.formDeclare,