@mattstack/rt-client 0.23.0 → 0.24.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.
@@ -123,8 +123,10 @@ export interface GateQuestion {
123
123
  multi: boolean;
124
124
  options: GateOption[];
125
125
  }
126
- export declare function gateOptionValue(o: GateOption): string;
127
- export declare function gateOptionLabel(o: GateOption): string;
126
+ /** Implementations live in gate-options.ts (the browser-safe ./gate
127
+ subpath); re-exported here so existing commands.ts/index.ts consumers
128
+ are unaffected. */
129
+ export { gateOptionValue, gateOptionLabel } from "./gate-options.ts";
128
130
  /** `session` is the answering surface's own session id, recorded so the
129
131
  push facility can tell a self-answer from a remote one and skip the
130
132
  doorbell it would otherwise send back to the writer. Optional: a caller
@@ -6,6 +6,8 @@ export interface GateOptionObject {
6
6
  value: string;
7
7
  label: string;
8
8
  }
9
+ export declare function gateOptionValue(o: GateOption): string;
10
+ export declare function gateOptionLabel(o: GateOption): string;
9
11
  /** Bare string s becomes {value: s, label: s}; a well-formed {value,label}
10
12
  object passes through untouched. Total over whatever actually arrives
11
13
  on the wire, not just the declared GateOption union: a partial object
package/dist/gate.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Browser-safe entry point (the "./gate" subpath export): the pure gate
3
+ * helpers only, so a browser bundle (mattstack-apps gate-kit) never drags in
4
+ * commands.ts's Node-only neighbors the way importing from "." would.
5
+ */
6
+ export * from "./gate-answers.ts";
7
+ export * from "./gate-options.ts";
8
+ export * from "./gate-presentation.ts";
9
+ export type { GateOption, GateQuestion, GateAnswer } from "./commands.ts";
package/dist/gate.js ADDED
@@ -0,0 +1,103 @@
1
+ // src/gate-options.ts
2
+ function gateOptionValue(o) {
3
+ return typeof o === "string" ? o : o.value;
4
+ }
5
+ function gateOptionLabel(o) {
6
+ return typeof o === "string" ? o : o.label || o.value;
7
+ }
8
+ var RECOMMENDED_SUFFIX = " (Recommended)";
9
+ var HAS_RECOMMENDED_SUFFIX = /\(\s*recommended\s*\)\s*$/i;
10
+ function isWordLikeLabel(label) {
11
+ return /^[a-z][^A-Z0-9/:\\@]*$/.test(label);
12
+ }
13
+ function capitalize(label) {
14
+ return isWordLikeLabel(label) ? label[0].toUpperCase() + label.slice(1) : label;
15
+ }
16
+ function normalizeGateOptions(options) {
17
+ return options.map((o) => {
18
+ const recommended = o !== null && typeof o === "object" && o.recommended === true;
19
+ let value;
20
+ let label;
21
+ if (typeof o === "string") {
22
+ value = o;
23
+ label = o;
24
+ } else if (o !== null && typeof o === "object") {
25
+ const v = typeof o.value === "string" ? o.value : undefined;
26
+ const l = typeof o.label === "string" ? o.label : undefined;
27
+ value = v ?? l ?? String(o);
28
+ label = l ?? v ?? String(o);
29
+ } else {
30
+ value = String(o);
31
+ label = String(o);
32
+ }
33
+ label = capitalize(label);
34
+ if (recommended && label && !HAS_RECOMMENDED_SUFFIX.test(label))
35
+ label += RECOMMENDED_SUFFIX;
36
+ return { value, label };
37
+ });
38
+ }
39
+ function normalizeGateQuestions(questions) {
40
+ return questions.map((q) => ({ ...q, options: normalizeGateOptions(q.options) }));
41
+ }
42
+
43
+ // src/gate-answers.ts
44
+ function unwrapGateAnswerValue(raw) {
45
+ if (raw && typeof raw === "object" && !Array.isArray(raw) && "value" in raw) {
46
+ return raw.value;
47
+ }
48
+ return raw;
49
+ }
50
+ function wrapperNoteIsValid(raw) {
51
+ if (!raw || typeof raw !== "object" || Array.isArray(raw) || !("value" in raw)) {
52
+ return true;
53
+ }
54
+ const note = raw.note;
55
+ return note === undefined || typeof note === "string";
56
+ }
57
+ function validateGateAnswers(questions, answers) {
58
+ const byId = new Map(questions.map((q) => [q.id, q]));
59
+ for (const [qid, raw] of Object.entries(answers)) {
60
+ const question = byId.get(qid);
61
+ if (!question)
62
+ return `unknown question id: ${qid}`;
63
+ if (!wrapperNoteIsValid(raw))
64
+ return `question ${qid} note must be a string`;
65
+ const value = unwrapGateAnswerValue(raw);
66
+ const isArray = Array.isArray(value);
67
+ if (question.multi && !isArray)
68
+ return `question ${qid} expects an array (multi)`;
69
+ if (!question.multi && isArray)
70
+ return `question ${qid} expects a single value`;
71
+ const values = isArray ? value : [value];
72
+ if (!values.every((v) => typeof v === "string"))
73
+ return `question ${qid} value must be a string`;
74
+ if (question.options.length > 0) {
75
+ const members = question.options.map(gateOptionValue);
76
+ for (const v of values) {
77
+ if (!members.includes(v))
78
+ return `answer for "${qid}" is not one of its options: "${v}"`;
79
+ }
80
+ }
81
+ }
82
+ const missing = questions.map((q) => q.id).filter((id) => !Object.prototype.hasOwnProperty.call(answers, id));
83
+ if (missing.length > 0)
84
+ return `missing answer(s) for: ${missing.join(", ")}`;
85
+ return null;
86
+ }
87
+ // src/gate-presentation.ts
88
+ var GATE_FORM_OPTION_CAP = 4;
89
+ function gatePresentation(args) {
90
+ if (!args.paneId || !args.sessionId)
91
+ return "wait";
92
+ return args.questions.every((q) => q.options.length <= GATE_FORM_OPTION_CAP) ? "form" : "wait";
93
+ }
94
+ export {
95
+ validateGateAnswers,
96
+ unwrapGateAnswerValue,
97
+ normalizeGateQuestions,
98
+ normalizeGateOptions,
99
+ gatePresentation,
100
+ gateOptionValue,
101
+ gateOptionLabel,
102
+ GATE_FORM_OPTION_CAP
103
+ };
package/dist/index.js CHANGED
@@ -418,14 +418,50 @@ function bgStop(o = {}) {
418
418
  function bgRelease(a, o = {}) {
419
419
  return rtCommand("bg:release", { claim: a.claim }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
420
420
  }
421
- // src/commands.ts
422
- var GATE_BY_PANE = "pane";
421
+ // src/gate-options.ts
423
422
  function gateOptionValue(o) {
424
423
  return typeof o === "string" ? o : o.value;
425
424
  }
426
425
  function gateOptionLabel(o) {
427
426
  return typeof o === "string" ? o : o.label || o.value;
428
427
  }
428
+ var RECOMMENDED_SUFFIX = " (Recommended)";
429
+ var HAS_RECOMMENDED_SUFFIX = /\(\s*recommended\s*\)\s*$/i;
430
+ function isWordLikeLabel(label) {
431
+ return /^[a-z][^A-Z0-9/:\\@]*$/.test(label);
432
+ }
433
+ function capitalize(label) {
434
+ return isWordLikeLabel(label) ? label[0].toUpperCase() + label.slice(1) : label;
435
+ }
436
+ function normalizeGateOptions(options) {
437
+ return options.map((o) => {
438
+ const recommended = o !== null && typeof o === "object" && o.recommended === true;
439
+ let value;
440
+ let label;
441
+ if (typeof o === "string") {
442
+ value = o;
443
+ label = o;
444
+ } else if (o !== null && typeof o === "object") {
445
+ const v = typeof o.value === "string" ? o.value : undefined;
446
+ const l = typeof o.label === "string" ? o.label : undefined;
447
+ value = v ?? l ?? String(o);
448
+ label = l ?? v ?? String(o);
449
+ } else {
450
+ value = String(o);
451
+ label = String(o);
452
+ }
453
+ label = capitalize(label);
454
+ if (recommended && label && !HAS_RECOMMENDED_SUFFIX.test(label))
455
+ label += RECOMMENDED_SUFFIX;
456
+ return { value, label };
457
+ });
458
+ }
459
+ function normalizeGateQuestions(questions) {
460
+ return questions.map((q) => ({ ...q, options: normalizeGateOptions(q.options) }));
461
+ }
462
+
463
+ // src/commands.ts
464
+ var GATE_BY_PANE = "pane";
429
465
  var COMMAND_NAMES = [
430
466
  "project-mrs:read",
431
467
  "discussions:read",
@@ -547,41 +583,6 @@ function gatePresentation(args) {
547
583
  return "wait";
548
584
  return args.questions.every((q) => q.options.length <= GATE_FORM_OPTION_CAP) ? "form" : "wait";
549
585
  }
550
- // src/gate-options.ts
551
- var RECOMMENDED_SUFFIX = " (Recommended)";
552
- var HAS_RECOMMENDED_SUFFIX = /\(\s*recommended\s*\)\s*$/i;
553
- function isWordLikeLabel(label) {
554
- return /^[a-z][^A-Z0-9/:\\@]*$/.test(label);
555
- }
556
- function capitalize(label) {
557
- return isWordLikeLabel(label) ? label[0].toUpperCase() + label.slice(1) : label;
558
- }
559
- function normalizeGateOptions(options) {
560
- return options.map((o) => {
561
- const recommended = o !== null && typeof o === "object" && o.recommended === true;
562
- let value;
563
- let label;
564
- if (typeof o === "string") {
565
- value = o;
566
- label = o;
567
- } else if (o !== null && typeof o === "object") {
568
- const v = typeof o.value === "string" ? o.value : undefined;
569
- const l = typeof o.label === "string" ? o.label : undefined;
570
- value = v ?? l ?? String(o);
571
- label = l ?? v ?? String(o);
572
- } else {
573
- value = String(o);
574
- label = String(o);
575
- }
576
- label = capitalize(label);
577
- if (recommended && label && !HAS_RECOMMENDED_SUFFIX.test(label))
578
- label += RECOMMENDED_SUFFIX;
579
- return { value, label };
580
- });
581
- }
582
- function normalizeGateQuestions(questions) {
583
- return questions.map((q) => ({ ...q, options: normalizeGateOptions(q.options) }));
584
- }
585
586
  // src/gate-answers.ts
586
587
  function unwrapGateAnswerValue(raw) {
587
588
  if (raw && typeof raw === "object" && !Array.isArray(raw) && "value" in raw) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mattstack/rt-client",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -15,6 +15,12 @@
15
15
  "import": "./dist/settings/identity-codec.js",
16
16
  "default": "./dist/settings/identity-codec.js"
17
17
  },
18
+ "./gate": {
19
+ "types": "./dist/gate.d.ts",
20
+ "bun": "./src/gate.ts",
21
+ "import": "./dist/gate.js",
22
+ "default": "./dist/gate.js"
23
+ },
18
24
  "./test/fake-daemon.ts": "./test/fake-daemon.ts"
19
25
  },
20
26
  "peerDependencies": {
@@ -45,7 +51,7 @@
45
51
  "bun": ">=1.0.0"
46
52
  },
47
53
  "scripts": {
48
- "build": "bun build src/index.ts --outdir dist --target node --format esm --packages external && bun build src/settings/identity-codec.ts --outfile dist/settings/identity-codec.js --target browser --format esm && tsc -p tsconfig.json",
54
+ "build": "bun build src/index.ts --outdir dist --target node --format esm --packages external && bun build src/settings/identity-codec.ts --outfile dist/settings/identity-codec.js --target browser --format esm && bun build src/gate.ts --outfile dist/gate.js --target browser --format esm && tsc -p tsconfig.json",
49
55
  "check-types": "tsc --noEmit -p tsconfig.json",
50
56
  "prepack": "bun run build"
51
57
  },
package/src/commands.ts CHANGED
@@ -107,12 +107,10 @@ export interface GateOrigin {
107
107
  presentation?: "form" | "wait";
108
108
  }
109
109
  export interface GateQuestion { id: string; label: string; multi: boolean; options: GateOption[] }
110
- export function gateOptionValue(o: GateOption): string {
111
- return typeof o === "string" ? o : o.value;
112
- }
113
- export function gateOptionLabel(o: GateOption): string {
114
- return typeof o === "string" ? o : (o.label || o.value);
115
- }
110
+ /** Implementations live in gate-options.ts (the browser-safe ./gate
111
+ subpath); re-exported here so existing commands.ts/index.ts consumers
112
+ are unaffected. */
113
+ export { gateOptionValue, gateOptionLabel } from "./gate-options.ts";
116
114
  /** `session` is the answering surface's own session id, recorded so the
117
115
  push facility can tell a self-answer from a remote one and skip the
118
116
  doorbell it would otherwise send back to the writer. Optional: a caller
@@ -1,5 +1,5 @@
1
1
  import type { GateQuestion, GateAnswer } from "./commands.ts";
2
- import { gateOptionValue } from "./commands.ts";
2
+ import { gateOptionValue } from "./gate-options.ts";
3
3
 
4
4
  export type GateAnswerWire = GateAnswer["answers"][string];
5
5
 
@@ -8,6 +8,13 @@ export interface GateOptionObject {
8
8
  label: string;
9
9
  }
10
10
 
11
+ export function gateOptionValue(o: GateOption): string {
12
+ return typeof o === "string" ? o : o.value;
13
+ }
14
+ export function gateOptionLabel(o: GateOption): string {
15
+ return typeof o === "string" ? o : (o.label || o.value);
16
+ }
17
+
11
18
  /** Suffix gate-kit's stripRecommended (mattstack-apps repo,
12
19
  packages/gate-kit/src/options.ts) parses off a label to render its own
13
20
  "recommended" badge. This is the
package/src/gate.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Browser-safe entry point (the "./gate" subpath export): the pure gate
3
+ * helpers only, so a browser bundle (mattstack-apps gate-kit) never drags in
4
+ * commands.ts's Node-only neighbors the way importing from "." would.
5
+ */
6
+ export * from "./gate-answers.ts";
7
+ export * from "./gate-options.ts";
8
+ export * from "./gate-presentation.ts";
9
+ export type { GateOption, GateQuestion, GateAnswer } from "./commands.ts";