@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.
- package/dist/commands.d.ts +4 -2
- package/dist/gate-options.d.ts +2 -0
- package/dist/gate.d.ts +9 -0
- package/dist/gate.js +103 -0
- package/dist/index.js +38 -37
- package/package.json +8 -2
- package/src/commands.ts +4 -6
- package/src/gate-answers.ts +1 -1
- package/src/gate-options.ts +7 -0
- package/src/gate.ts +9 -0
package/dist/commands.d.ts
CHANGED
|
@@ -123,8 +123,10 @@ export interface GateQuestion {
|
|
|
123
123
|
multi: boolean;
|
|
124
124
|
options: GateOption[];
|
|
125
125
|
}
|
|
126
|
-
|
|
127
|
-
|
|
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
|
package/dist/gate-options.d.ts
CHANGED
|
@@ -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/
|
|
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.
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
export
|
|
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
|
package/src/gate-answers.ts
CHANGED
package/src/gate-options.ts
CHANGED
|
@@ -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";
|