@ouronet/talos-registry 1.1.1 → 1.3.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/index.d.ts CHANGED
@@ -16,4 +16,6 @@ export type { CallPlan } from "./plan.js";
16
16
  export { parseCapability, parseCapabilities, capabilityRecipe } from "./caps.js";
17
17
  export type { ParsedCapability, CapabilityRecipe } from "./caps.js";
18
18
  export { formatForType, formatDecimal, formatInteger, formatString, formatBool } from "./format.js";
19
+ export { tooltipModel, tooltipModels, signatureRows, isNativeKey, isPlaceholder, PLACEHOLDER, NATIVE_SIGNATURES } from "./tooltip.js";
20
+ export type { TooltipModel, TooltipSlot, TooltipPreview } from "./tooltip.js";
19
21
  export type * from "./types.js";
package/dist/index.js CHANGED
@@ -13,3 +13,6 @@ export { buildCall, buildPreviewCall, buildGhostCall } from "./build.js";
13
13
  export { planCall, explainCall } from "./plan.js";
14
14
  export { parseCapability, parseCapabilities, capabilityRecipe } from "./caps.js";
15
15
  export { formatForType, formatDecimal, formatInteger, formatString, formatBool } from "./format.js";
16
+ // THE TOOLTIP CANON. Shared implementation rather than a shared document -- see TOOLTIP-CANON.md
17
+ // for why each rule exists, and `tooltip.ts` for the rules themselves.
18
+ export { tooltipModel, tooltipModels, signatureRows, isNativeKey, isPlaceholder, PLACEHOLDER, NATIVE_SIGNATURES } from "./tooltip.js";
@@ -0,0 +1,91 @@
1
+ /** One parameter of the execution, with everything a renderer needs about it. */
2
+ export interface TooltipSlot {
3
+ /** 1-based, so a renderer never has to decide whether to add one. */
4
+ index: number;
5
+ name: string;
6
+ /** the Pact type as DECLARED. Rendered on its own row -- rule 3. */
7
+ type: string;
8
+ /** the rendered Pact literal, or the `display` override where one exists. */
9
+ value: string;
10
+ /** the generic "example" ghost: an entity that does not exist on chain. Render it as such. */
11
+ isPlaceholder: boolean;
12
+ /** the output of a preflight read. There is no value and none may be invented. */
13
+ isPreflightFed: boolean;
14
+ /** false = never prefill into an input that can reach a signed transaction (guards). */
15
+ prefillable: boolean;
16
+ /** the registry's own note about this argument, when it has one. */
17
+ note?: string;
18
+ }
19
+ export interface TooltipPreview {
20
+ /** fully qualified INFO_ reader. */
21
+ name: string;
22
+ /** ITS parameter names, which are usually not the execution's. */
23
+ params: string[];
24
+ /** the rendered call, ready to `/local`. */
25
+ call: string;
26
+ }
27
+ export interface TooltipModel {
28
+ /** `native` renders with a gold perimeter and no cost section -- rule 4. */
29
+ kind: "ouronet" | "native";
30
+ /** fully qualified execution, as it would be typed in a transaction. */
31
+ exec: string;
32
+ /** EVERY execution parameter, in declared order -- rule 1. */
33
+ slots: TooltipSlot[];
34
+ /** absent for native calls: nothing on StoaChain's `coin` contract prices a call. */
35
+ preview?: TooltipPreview;
36
+ /**
37
+ * Whether a renderer should fire the preview read.
38
+ *
39
+ * False when any argument is a placeholder -- the read would refuse, and a refusal per hover is
40
+ * latency the user pays for a foregone conclusion. Rule 5.
41
+ */
42
+ shouldRead: boolean;
43
+ /** things a renderer may want to surface. Never thrown; a tooltip must not take a page down. */
44
+ warnings: string[];
45
+ }
46
+ /** The registry's generic placeholder: an entity id for something that does not exist on chain. */
47
+ export declare const PLACEHOLDER = "\"example\"";
48
+ export declare const isPlaceholder: (rendered: string) => boolean;
49
+ /** A `coin.*` key is StoaChain's root-namespace contract -- outside this registry by design. */
50
+ export declare const isNativeKey: (key: string) => boolean;
51
+ /**
52
+ * STOA-native specs, which the generated registry does not and will not describe.
53
+ *
54
+ * They live HERE rather than in each consumer so that both applications render the same
55
+ * signatures. Transcribed from the deployed contract with its line numbers; `tooltip.test.ts`
56
+ * asserts internal consistency and the Pact repo's own test re-reads the source.
57
+ *
58
+ * source: 0_Stoa/coin-contract/coin-live.pact
59
+ */
60
+ export declare const NATIVE_SIGNATURES: Readonly<Record<string, ReadonlyArray<[string, string]>>>;
61
+ /**
62
+ * Everything a renderer needs for one entrypoint.
63
+ *
64
+ * `values` overrides ghosts BY PARAMETER NAME. Never positionally: 410 of 423 previews declare a
65
+ * different list from their entrypoint, and positional merging is how an executor's account ends
66
+ * up in a token-id slot -- silently, because both are strings.
67
+ *
68
+ * Throws only on an unknown key, which is a programming error. Everything else degrades: an
69
+ * unresolvable argument becomes a visible placeholder rather than an exception, because a tooltip
70
+ * must never take down the page it annotates.
71
+ */
72
+ export declare function tooltipModel(key: string, values?: Readonly<Record<string, string>>): TooltipModel;
73
+ /**
74
+ * Models for a button that fronts a CHOICE of executions, in the order the ZBOM presents them.
75
+ *
76
+ * Unknown keys are DROPPED rather than throwing: a button offering three executions should not
77
+ * lose its tooltip because one is stale. The absence is reported in `warnings` of the survivors'
78
+ * caller instead -- and if nothing survives, the caller renders no tooltip, which is the correct
79
+ * outcome and not a crash.
80
+ */
81
+ export declare function tooltipModels(keys: readonly string[], values?: Readonly<Record<string, string>>): TooltipModel[];
82
+ /**
83
+ * The type row -- rule 3.
84
+ *
85
+ * Returned parallel to the names so a renderer can print two aligned lines rather than
86
+ * `name:type` pairs, which double the width of the widest line in the panel.
87
+ */
88
+ export declare function signatureRows(m: TooltipModel): {
89
+ names: string[];
90
+ types: string[];
91
+ };
@@ -0,0 +1,226 @@
1
+ /**
2
+ * THE TOOLTIP CANON, as code.
3
+ *
4
+ * A pre-ZBOM tooltip answers one question: *what will this button actually execute, and what will
5
+ * it cost?* Two applications render it -- OuronetUI and the Codex -- and they had been converging
6
+ * on it independently, each rediscovering the same traps a few weeks apart. This module exists so
7
+ * the rules are a SHARED IMPLEMENTATION rather than a shared document, because a document drifts
8
+ * from the code it describes and nobody finds out until a user sees a wrong number.
9
+ *
10
+ * `TOOLTIP-CANON.md` beside this file explains WHY each rule exists. This file is the rule.
11
+ *
12
+ * ── THE FIVE RULES ────────────────────────────────────────────────────────────────────────────
13
+ *
14
+ * 1. SHOW EVERY EXECUTION PARAMETER. All of them, always, in declared order. A tooltip that lists
15
+ * five rows for a six-parameter function is not a summary, it is a wrong answer -- and it is
16
+ * how `DPTF|C_Transfer` came to display five arguments for a six-argument call.
17
+ *
18
+ * 2. THE ARGUMENTS BELONG TO THE EXECUTION, the cost belongs to the preview. 410 of 423
19
+ * entrypoints have a preview whose parameter list DIFFERS from their own, so the two cannot
20
+ * share one numbered list. Render the execution's; mention the preview's separately.
21
+ *
22
+ * 3. TYPES GO ON THEIR OWN ROW. `patron:string executor:string ...` doubles the width of the
23
+ * widest line in the panel. A parallel row under the names costs one line and reads better.
24
+ *
25
+ * 4. NATIVE IS NOT OURONET, and the difference is narrow. A `coin.*` call has no INFO_ preview
26
+ * and collects no IGNIS. Gas IS still sponsored -- claiming otherwise tells a user they are
27
+ * about to spend when they are not. Render native with a gold perimeter so the two are never
28
+ * confused, and say what is actually different rather than inventing a difference.
29
+ *
30
+ * 5. A GHOST IS NOT DATA. An unresolved argument must LOOK unresolved, and a tooltip whose
31
+ * arguments are placeholders must not fire the preview read at all: the answer is a foregone
32
+ * refusal and the latency is paid by the user for nothing.
33
+ */
34
+ import { getEntrypoint, getPreview, tryGetEntrypoint, registry } from "./registry.js";
35
+ import { formatForType } from "./format.js";
36
+ /** The registry's generic placeholder: an entity id for something that does not exist on chain. */
37
+ export const PLACEHOLDER = '"example"';
38
+ export const isPlaceholder = (rendered) => rendered === PLACEHOLDER;
39
+ /** A `coin.*` key is StoaChain's root-namespace contract -- outside this registry by design. */
40
+ export const isNativeKey = (key) => key.startsWith("coin.");
41
+ /**
42
+ * STOA-native specs, which the generated registry does not and will not describe.
43
+ *
44
+ * They live HERE rather than in each consumer so that both applications render the same
45
+ * signatures. Transcribed from the deployed contract with its line numbers; `tooltip.test.ts`
46
+ * asserts internal consistency and the Pact repo's own test re-reads the source.
47
+ *
48
+ * source: 0_Stoa/coin-contract/coin-live.pact
49
+ */
50
+ export const NATIVE_SIGNATURES = {
51
+ // [name, type] pairs, in declared order.
52
+ "coin.C_Transfer": [["sender", "string"], ["receiver", "string"], ["amount", "decimal"]], // :583
53
+ "coin.C_Transmit": [["sender", "string"], ["receiver", "string"], ["amount", "decimal"]], // :593
54
+ "coin.C_TransferAnew": [["sender", "string"], ["receiver", "string"], ["receiver-guard", "guard"], ["amount", "decimal"]], // :586
55
+ "coin.C_TransmitAnew": [["sender", "string"], ["receiver", "string"], ["receiver-guard", "guard"], ["amount", "decimal"]], // :599
56
+ "coin.C_UR|Transfer": [["sender", "string"], ["receiver", "string"], ["amount", "decimal"]], // :1075
57
+ "coin.C_UR|Transmit": [["sender", "string"], ["receiver", "string"], ["amount", "decimal"]], // :1085
58
+ "coin.C_URV|Stake": [["account", "string"], ["urstoa-amount", "decimal"]], // :1407
59
+ "coin.C_URV|Unstake": [["account", "string"], ["urstoa-amount", "decimal"]], // :1439
60
+ "coin.C_URV|Collect": [["account", "string"]], // :1467
61
+ };
62
+ /** Kadena-shaped, NOT an Ouronet glyph account -- a `coin` call takes k:/c:/u:/w:. */
63
+ const NATIVE_GHOST = {
64
+ string: '"k:1ac0d8b0a4f6e2c9d3b5a7e1f4c6089d2b3e5a7c9f1d3b5e7a9c1f3d5b7e9a1c"',
65
+ decimal: "1.0",
66
+ integer: "1",
67
+ bool: "false",
68
+ guard: '(read-keyset "ks")',
69
+ };
70
+ function nativeModel(key, values) {
71
+ // Callers reach this only through `tooltipModel`, which has already rejected an unknown key.
72
+ // Asserted rather than assumed: a non-null assertion here would make a future direct caller
73
+ // fail with a property access on undefined instead of a sentence naming the problem.
74
+ const sig = NATIVE_SIGNATURES[key];
75
+ if (!sig)
76
+ throw new Error(`nativeModel: no signature for "${key}"`);
77
+ return {
78
+ kind: "native",
79
+ exec: key,
80
+ slots: sig.map(([name, type], i) => ({
81
+ index: i + 1,
82
+ name,
83
+ type,
84
+ value: values[name] ?? NATIVE_GHOST[type] ?? PLACEHOLDER,
85
+ isPlaceholder: values[name] === undefined && NATIVE_GHOST[type] === undefined,
86
+ isPreflightFed: false,
87
+ // A guard is never prefillable, native or not. Same rule, same reason.
88
+ prefillable: type !== "guard",
89
+ })),
90
+ // No preview, deliberately. Rule 4: nothing on `coin` prices a call, so there is no cost to
91
+ // show and a renderer must say that rather than leaving an empty panel that reads as a
92
+ // failed load.
93
+ shouldRead: false,
94
+ warnings: [],
95
+ };
96
+ }
97
+ /**
98
+ * Everything a renderer needs for one entrypoint.
99
+ *
100
+ * `values` overrides ghosts BY PARAMETER NAME. Never positionally: 410 of 423 previews declare a
101
+ * different list from their entrypoint, and positional merging is how an executor's account ends
102
+ * up in a token-id slot -- silently, because both are strings.
103
+ *
104
+ * Throws only on an unknown key, which is a programming error. Everything else degrades: an
105
+ * unresolvable argument becomes a visible placeholder rather than an exception, because a tooltip
106
+ * must never take down the page it annotates.
107
+ */
108
+ export function tooltipModel(key, values = {}) {
109
+ if (isNativeKey(key)) {
110
+ if (!NATIVE_SIGNATURES[key])
111
+ throw new Error(`tooltipModel: unknown native key "${key}". ` +
112
+ `Known: ${Object.keys(NATIVE_SIGNATURES).join(", ")}`);
113
+ return nativeModel(key, values);
114
+ }
115
+ const ep = getEntrypoint(key); // throws, and names near matches, on a stale key
116
+ const warnings = [];
117
+ const use = ep.ghost.use ?? {};
118
+ // RULE 1: every parameter, in declared order. Not the preview's list, not a subset.
119
+ const slots = ep.params.map((p, i) => {
120
+ const supplied = values[p.name];
121
+ const raw = ep.ghost.args[p.name];
122
+ const u = use[p.name];
123
+ let value;
124
+ let isPlaceholder = false;
125
+ const isPreflightFed = raw === null;
126
+ if (supplied !== undefined) {
127
+ value = supplied;
128
+ }
129
+ else if (u?.display !== undefined) {
130
+ // The submittable form and the readable form differ -- a guard is `(read-keyset "ks")` to
131
+ // a reader and a data-key name to the transaction builder.
132
+ value = u.display;
133
+ }
134
+ else if (isPreflightFed) {
135
+ value = "<from preflight>";
136
+ }
137
+ else if (raw === undefined) {
138
+ value = PLACEHOLDER;
139
+ isPlaceholder = true;
140
+ warnings.push(`no ghost for ${p.name}:${p.type}`);
141
+ }
142
+ else {
143
+ try {
144
+ value = formatForType(raw, p.type);
145
+ }
146
+ catch {
147
+ value = PLACEHOLDER;
148
+ isPlaceholder = true;
149
+ warnings.push(`ghost for ${p.name} does not fit ${p.type}`);
150
+ }
151
+ if (isPlaceholder || value === PLACEHOLDER)
152
+ isPlaceholder = true;
153
+ }
154
+ return {
155
+ index: i + 1,
156
+ name: p.name,
157
+ type: p.type,
158
+ value,
159
+ isPlaceholder,
160
+ isPreflightFed,
161
+ prefillable: u?.zbom !== false,
162
+ note: ep.ghost.notes?.[p.name],
163
+ };
164
+ });
165
+ // RULE 2: the preview is priced with ITS OWN parameter list, which is usually not this one.
166
+ let preview;
167
+ const pvKey = ep.preview;
168
+ const pv = pvKey ? getPreview(pvKey) : undefined;
169
+ if (pvKey && pv) {
170
+ const args = pv.params.map((p) => {
171
+ const supplied = values[p.name];
172
+ if (supplied !== undefined)
173
+ return supplied;
174
+ const raw = ep.ghost.args[p.name];
175
+ if (raw === undefined || raw === null)
176
+ return PLACEHOLDER;
177
+ try {
178
+ return formatForType(raw, p.type);
179
+ }
180
+ catch {
181
+ return PLACEHOLDER;
182
+ }
183
+ });
184
+ preview = {
185
+ name: `${registry.namespace}.${pvKey}`,
186
+ params: pv.params.map((p) => p.name),
187
+ call: `(${registry.namespace}.${pvKey} ${args.join(" ")})`,
188
+ };
189
+ if (args.some(isPlaceholder))
190
+ warnings.push("preview has placeholder arguments; the read would refuse");
191
+ }
192
+ else if (pvKey) {
193
+ warnings.push(`preview ${pvKey} is named but not in the registry`);
194
+ }
195
+ return {
196
+ kind: "ouronet",
197
+ exec: `${registry.namespace}.${key}`,
198
+ slots,
199
+ preview,
200
+ // RULE 5: do not fire a read whose answer is a foregone refusal.
201
+ shouldRead: preview !== undefined && !preview.call.includes(PLACEHOLDER),
202
+ warnings,
203
+ };
204
+ }
205
+ /**
206
+ * Models for a button that fronts a CHOICE of executions, in the order the ZBOM presents them.
207
+ *
208
+ * Unknown keys are DROPPED rather than throwing: a button offering three executions should not
209
+ * lose its tooltip because one is stale. The absence is reported in `warnings` of the survivors'
210
+ * caller instead -- and if nothing survives, the caller renders no tooltip, which is the correct
211
+ * outcome and not a crash.
212
+ */
213
+ export function tooltipModels(keys, values = {}) {
214
+ return keys
215
+ .filter((k) => isNativeKey(k) ? Boolean(NATIVE_SIGNATURES[k]) : Boolean(tryGetEntrypoint(k)))
216
+ .map((k) => tooltipModel(k, values));
217
+ }
218
+ /**
219
+ * The type row -- rule 3.
220
+ *
221
+ * Returned parallel to the names so a renderer can print two aligned lines rather than
222
+ * `name:type` pairs, which double the width of the widest line in the panel.
223
+ */
224
+ export function signatureRows(m) {
225
+ return { names: m.slots.map((s) => s.name), types: m.slots.map((s) => s.type) };
226
+ }
package/dist/types.d.ts CHANGED
@@ -104,11 +104,26 @@ export interface ExternalCaps {
104
104
  attachTo: string;
105
105
  note: string;
106
106
  }
107
+ /** Where a single ghost value may be used. Absent field = permitted. */
108
+ export interface GhostUse {
109
+ /** false = MUST NOT be prefilled into an input that can reach a signed transaction. */
110
+ zbom?: boolean;
111
+ /** false = must not be rendered at all. */
112
+ tooltip?: boolean;
113
+ /** render this string instead of the value (the submittable and readable forms differ). */
114
+ display?: string;
115
+ }
107
116
  export interface Ghost {
108
117
  /** example arguments by parameter name. `null` means "obtain from the preflight". */
109
118
  args: Record<string, unknown>;
110
119
  source: string;
111
120
  notes?: Record<string, string>;
121
+ /**
122
+ * Per-parameter surface restrictions. Present ONLY for parameters that have one -- 6 of 423
123
+ * entrypoints today, all guard-taking. An absent parameter is unrestricted.
124
+ */
125
+ use?: Record<string, GhostUse>;
126
+ useNote?: string;
112
127
  unresolved?: string[];
113
128
  unresolvedNote?: string;
114
129
  warning: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouronet/talos-registry",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "The Ouronet callable surface, generated from the deployed contracts. Supply values; never type a function name.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,8 @@
18
18
  "files": [
19
19
  "dist",
20
20
  "README.md",
21
- "CHANGELOG.md"
21
+ "CHANGELOG.md",
22
+ "TOOLTIP-CANON.md"
22
23
  ],
23
24
  "scripts": {
24
25
  "sync": "node scripts/sync-registry.mjs",