@mattstack/rt-client 0.29.0 → 0.30.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.
@@ -153,6 +153,7 @@ export interface GateAnswer {
153
153
  answers: Record<string, string | string[] | {
154
154
  value: string | string[];
155
155
  note?: string;
156
+ text?: string;
156
157
  }>;
157
158
  by: string;
158
159
  answeredAt: number;
@@ -1,7 +1,8 @@
1
1
  import type { GateQuestion, GateAnswer } from "./commands.ts";
2
2
  export type GateAnswerWire = GateAnswer["answers"][string];
3
- /** Both wire shapes carry the same value underneath: bare, or {value, note?}
4
- when a panel attaches free text. Validation reads only the value. */
3
+ /** Both wire shapes carry the same value underneath: bare, or {value,
4
+ note?, text?} when a panel attaches free text or a replacement for text
5
+ the gate offered. Unwrapping keeps only the value. */
5
6
  export declare function unwrapGateAnswerValue(raw: unknown): unknown;
6
7
  /** Option membership is required whenever a question declares options,
7
8
  checked against the unwrapped value (every element, for multi); an
package/dist/gate.js CHANGED
@@ -54,12 +54,18 @@ function unwrapGateAnswerValue(raw) {
54
54
  }
55
55
  return raw;
56
56
  }
57
- function wrapperNoteIsValid(raw) {
57
+ function wrapperFieldError(qid, raw) {
58
58
  if (!raw || typeof raw !== "object" || Array.isArray(raw) || !("value" in raw)) {
59
- return true;
59
+ return null;
60
60
  }
61
- const note = raw.note;
62
- return note === undefined || typeof note === "string";
61
+ const { note, text } = raw;
62
+ if (note !== undefined && typeof note !== "string")
63
+ return `question ${qid} note must be a string`;
64
+ if (text !== undefined && typeof text !== "string")
65
+ return `question ${qid} text must be a string`;
66
+ if (typeof text === "string" && text.trim() === "")
67
+ return `question ${qid} text must not be empty`;
68
+ return null;
63
69
  }
64
70
  function validateGateAnswers(questions, answers) {
65
71
  const byId = new Map(questions.map((q) => [q.id, q]));
@@ -67,8 +73,9 @@ function validateGateAnswers(questions, answers) {
67
73
  const question = byId.get(qid);
68
74
  if (!question)
69
75
  return `unknown question id: ${qid}`;
70
- if (!wrapperNoteIsValid(raw))
71
- return `question ${qid} note must be a string`;
76
+ const wrapperError = wrapperFieldError(qid, raw);
77
+ if (wrapperError)
78
+ return wrapperError;
72
79
  const value = unwrapGateAnswerValue(raw);
73
80
  const isArray = Array.isArray(value);
74
81
  if (question.multi && !isArray)
package/dist/index.js CHANGED
@@ -643,12 +643,18 @@ function unwrapGateAnswerValue(raw) {
643
643
  }
644
644
  return raw;
645
645
  }
646
- function wrapperNoteIsValid(raw) {
646
+ function wrapperFieldError(qid, raw) {
647
647
  if (!raw || typeof raw !== "object" || Array.isArray(raw) || !("value" in raw)) {
648
- return true;
648
+ return null;
649
649
  }
650
- const note = raw.note;
651
- return note === undefined || typeof note === "string";
650
+ const { note, text } = raw;
651
+ if (note !== undefined && typeof note !== "string")
652
+ return `question ${qid} note must be a string`;
653
+ if (text !== undefined && typeof text !== "string")
654
+ return `question ${qid} text must be a string`;
655
+ if (typeof text === "string" && text.trim() === "")
656
+ return `question ${qid} text must not be empty`;
657
+ return null;
652
658
  }
653
659
  function validateGateAnswers(questions, answers) {
654
660
  const byId = new Map(questions.map((q) => [q.id, q]));
@@ -656,8 +662,9 @@ function validateGateAnswers(questions, answers) {
656
662
  const question = byId.get(qid);
657
663
  if (!question)
658
664
  return `unknown question id: ${qid}`;
659
- if (!wrapperNoteIsValid(raw))
660
- return `question ${qid} note must be a string`;
665
+ const wrapperError = wrapperFieldError(qid, raw);
666
+ if (wrapperError)
667
+ return wrapperError;
661
668
  const value = unwrapGateAnswerValue(raw);
662
669
  const isArray = Array.isArray(value);
663
670
  if (question.multi && !isArray)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mattstack/rt-client",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/commands.ts CHANGED
@@ -133,7 +133,7 @@ export { gateOptionValue, gateOptionLabel } from "./gate-options.ts";
133
133
  doorbell it would otherwise send back to the writer. Optional: a caller
134
134
  that supplies none (the board status-bin answers `by: "pane"` with no
135
135
  session) still matches self by `by === GATE_BY_PANE`. */
136
- export interface GateAnswer { answers: Record<string, string | string[] | { value: string | string[]; note?: string }>; by: string; answeredAt: number; overridden?: boolean; session?: string }
136
+ export interface GateAnswer { answers: Record<string, string | string[] | { value: string | string[]; note?: string; text?: string }>; by: string; answeredAt: number; overridden?: boolean; session?: string }
137
137
  export interface GateRow {
138
138
  id: string; subject: string; kind: string;
139
139
  questions: GateQuestion[]; meta: Record<string, unknown> | null;
@@ -3,8 +3,9 @@ import { gateOptionValue } from "./gate-options.ts";
3
3
 
4
4
  export type GateAnswerWire = GateAnswer["answers"][string];
5
5
 
6
- /** Both wire shapes carry the same value underneath: bare, or {value, note?}
7
- when a panel attaches free text. Validation reads only the value. */
6
+ /** Both wire shapes carry the same value underneath: bare, or {value,
7
+ note?, text?} when a panel attaches free text or a replacement for text
8
+ the gate offered. Unwrapping keeps only the value. */
8
9
  export function unwrapGateAnswerValue(raw: unknown): unknown {
9
10
  if (raw && typeof raw === "object" && !Array.isArray(raw) && "value" in (raw as Record<string, unknown>)) {
10
11
  return (raw as { value: unknown }).value;
@@ -12,12 +13,15 @@ export function unwrapGateAnswerValue(raw: unknown): unknown {
12
13
  return raw;
13
14
  }
14
15
 
15
- function wrapperNoteIsValid(raw: unknown): boolean {
16
+ function wrapperFieldError(qid: string, raw: unknown): string | null {
16
17
  if (!raw || typeof raw !== "object" || Array.isArray(raw) || !("value" in (raw as Record<string, unknown>))) {
17
- return true;
18
+ return null;
18
19
  }
19
- const note = (raw as Record<string, unknown>).note;
20
- return note === undefined || typeof note === "string";
20
+ const { note, text } = raw as Record<string, unknown>;
21
+ if (note !== undefined && typeof note !== "string") return `question ${qid} note must be a string`;
22
+ if (text !== undefined && typeof text !== "string") return `question ${qid} text must be a string`;
23
+ if (typeof text === "string" && text.trim() === "") return `question ${qid} text must not be empty`;
24
+ return null;
21
25
  }
22
26
 
23
27
  /** Option membership is required whenever a question declares options,
@@ -32,7 +36,8 @@ export function validateGateAnswers(
32
36
  for (const [qid, raw] of Object.entries(answers)) {
33
37
  const question = byId.get(qid);
34
38
  if (!question) return `unknown question id: ${qid}`;
35
- if (!wrapperNoteIsValid(raw)) return `question ${qid} note must be a string`;
39
+ const wrapperError = wrapperFieldError(qid, raw);
40
+ if (wrapperError) return wrapperError;
36
41
  const value = unwrapGateAnswerValue(raw);
37
42
  const isArray = Array.isArray(value);
38
43
  if (question.multi && !isArray) return `question ${qid} expects an array (multi)`;