@assistant-ui/react-generative-ui 0.0.15 → 0.0.16
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/convert/boundSpec.d.ts +47 -0
- package/dist/convert/boundSpec.d.ts.map +1 -0
- package/dist/convert/boundSpec.js +99 -0
- package/dist/convert/boundSpec.js.map +1 -0
- package/dist/convert/copyBounded.d.ts +28 -0
- package/dist/convert/copyBounded.d.ts.map +1 -0
- package/dist/convert/copyBounded.js +38 -0
- package/dist/convert/copyBounded.js.map +1 -0
- package/dist/convert/isElement.d.ts +6 -0
- package/dist/convert/isElement.d.ts.map +1 -0
- package/dist/convert/isElement.js +6 -0
- package/dist/convert/isElement.js.map +1 -0
- package/dist/convert/takeRun.d.ts +9 -0
- package/dist/convert/takeRun.d.ts.map +1 -0
- package/dist/convert/takeRun.js +20 -0
- package/dist/convert/takeRun.js.map +1 -0
- package/dist/slack/constants.d.ts +1 -26
- package/dist/slack/constants.d.ts.map +1 -1
- package/dist/slack/constants.js +1 -26
- package/dist/slack/constants.js.map +1 -1
- package/dist/slack/fromSlackBlocks.js +6 -10
- package/dist/slack/fromSlackBlocks.js.map +1 -1
- package/dist/slack/toSlackBlocks.d.ts.map +1 -1
- package/dist/slack/toSlackBlocks.js +28 -95
- package/dist/slack/toSlackBlocks.js.map +1 -1
- package/dist/teams/constants.d.ts +1 -28
- package/dist/teams/constants.d.ts.map +1 -1
- package/dist/teams/constants.js +1 -32
- package/dist/teams/constants.js.map +1 -1
- package/dist/teams/toAdaptiveCard.d.ts.map +1 -1
- package/dist/teams/toAdaptiveCard.js +12 -23
- package/dist/teams/toAdaptiveCard.js.map +1 -1
- package/dist/teams/toTeamsAttachments.d.ts.map +1 -1
- package/dist/teams/toTeamsAttachments.js +3 -4
- package/dist/teams/toTeamsAttachments.js.map +1 -1
- package/dist/vocabulary/list.js +4 -2
- package/dist/vocabulary/list.js.map +1 -1
- package/package.json +6 -6
- package/src/convert/boundSpec.test.ts +147 -0
- package/src/{teams → convert}/boundSpec.ts +48 -14
- package/src/convert/copyBounded.test.ts +131 -0
- package/src/convert/copyBounded.ts +38 -0
- package/src/convert/isElement.ts +6 -0
- package/src/convert/takeRun.test.ts +64 -0
- package/src/convert/takeRun.ts +19 -0
- package/src/slack/constants.ts +0 -29
- package/src/slack/fromSlackBlocks.test.ts +105 -7
- package/src/slack/fromSlackBlocks.ts +9 -9
- package/src/slack/toSlackBlocks.test.ts +82 -2
- package/src/slack/toSlackBlocks.ts +68 -164
- package/src/teams/constants.ts +0 -42
- package/src/teams/toAdaptiveCard.test.ts +74 -2
- package/src/teams/toAdaptiveCard.ts +29 -36
- package/src/teams/toTeamsAttachments.ts +2 -8
- package/src/vocabulary/list.test.tsx +35 -9
- package/src/vocabulary/list.tsx +4 -2
- package/dist/teams/boundSpec.d.ts +0 -20
- package/dist/teams/boundSpec.d.ts.map +0 -1
- package/dist/teams/boundSpec.js +0 -67
- package/dist/teams/boundSpec.js.map +0 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/convert/boundSpec.d.ts
|
|
2
|
+
/** The maximum traversal depth, counted in visited values, not tree levels. */
|
|
3
|
+
declare const MAX_TRAVERSAL_DEPTH = 64;
|
|
4
|
+
/**
|
|
5
|
+
* The element-nesting ceiling a caller observes. `boundNode` visits an element
|
|
6
|
+
* and its `children` array separately, so one level of nesting spends two of
|
|
7
|
+
* {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never
|
|
8
|
+
* the looser of the two. A root array costs one more level than a root object.
|
|
9
|
+
*/
|
|
10
|
+
declare const MAX_ELEMENT_DEPTH: number;
|
|
11
|
+
/**
|
|
12
|
+
* The maximum number of entries kept at any single level (root, or a
|
|
13
|
+
* `children` array at any depth) of the raw spec, before it ever reaches
|
|
14
|
+
* `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
|
|
15
|
+
* can never be made to walk further than this regardless of what the array
|
|
16
|
+
* claims about itself.
|
|
17
|
+
*/
|
|
18
|
+
declare const CHILDREN_CAP = 200;
|
|
19
|
+
/**
|
|
20
|
+
* The total number of nodes {@link boundSpec} will visit across one walk,
|
|
21
|
+
* regardless of how many times a shared reference recurs. Bounds the
|
|
22
|
+
* combinatorial work a DAG of shared or self-referential nodes would
|
|
23
|
+
* otherwise force even though each individual array stays within
|
|
24
|
+
* {@link CHILDREN_CAP}.
|
|
25
|
+
*/
|
|
26
|
+
declare const NODE_BUDGET = 5000;
|
|
27
|
+
type ClampReason = "children" | "budget" | "cycle" | "depth";
|
|
28
|
+
/** Renders the warning detail a protocol reports for one {@link ClampReason}. */
|
|
29
|
+
declare function clampReasonDetail(reason: ClampReason): string;
|
|
30
|
+
/**
|
|
31
|
+
* Produces a bounded plain copy of a raw generative-ui spec before it
|
|
32
|
+
* reaches `normalizeSpec`, whose own traversal of the root array or any
|
|
33
|
+
* `children` array walks the full reported length of a hostile proxied
|
|
34
|
+
* array before any per-field cap downstream ever applies. Every array
|
|
35
|
+
* (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}
|
|
36
|
+
* entries by index, which bounds a proxy however it fabricates `length` or
|
|
37
|
+
* replaces its array methods; recursion itself is capped at
|
|
38
|
+
* {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was
|
|
39
|
+
* truncated, receiving the reason for that truncation: `"children"`,
|
|
40
|
+
* `"depth"`, `"budget"`, or `"cycle"`. The walk also spends a total budget of
|
|
41
|
+
* {@link NODE_BUDGET} nodes, so shared references cannot multiply work
|
|
42
|
+
* exponentially, and a node that is its own ancestor is cut to `null`.
|
|
43
|
+
*/
|
|
44
|
+
declare function boundSpec(spec: unknown, onClamp: (reason: ClampReason) => void): unknown;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { CHILDREN_CAP, ClampReason, MAX_ELEMENT_DEPTH, MAX_TRAVERSAL_DEPTH, NODE_BUDGET, boundSpec, clampReasonDetail };
|
|
47
|
+
//# sourceMappingURL=boundSpec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundSpec.d.ts","names":[],"sources":["../../src/convert/boundSpec.ts"],"mappings":";;cAGa;;;;;;;cAQA;;;;;;;;cASA;;;;;;;;cASA;KAED;;iBAGI,kBAAkB,QAAQ;;;;;;;;;;;;;;;iBA0F1B,UACd,eACA,UAAU,QAAQ"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { copyBounded } from "./copyBounded.js";
|
|
2
|
+
//#region src/convert/boundSpec.ts
|
|
3
|
+
/** The maximum traversal depth, counted in visited values, not tree levels. */
|
|
4
|
+
const MAX_TRAVERSAL_DEPTH = 64;
|
|
5
|
+
/**
|
|
6
|
+
* The element-nesting ceiling a caller observes. `boundNode` visits an element
|
|
7
|
+
* and its `children` array separately, so one level of nesting spends two of
|
|
8
|
+
* {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never
|
|
9
|
+
* the looser of the two. A root array costs one more level than a root object.
|
|
10
|
+
*/
|
|
11
|
+
const MAX_ELEMENT_DEPTH = 32;
|
|
12
|
+
/**
|
|
13
|
+
* The maximum number of entries kept at any single level (root, or a
|
|
14
|
+
* `children` array at any depth) of the raw spec, before it ever reaches
|
|
15
|
+
* `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
|
|
16
|
+
* can never be made to walk further than this regardless of what the array
|
|
17
|
+
* claims about itself.
|
|
18
|
+
*/
|
|
19
|
+
const CHILDREN_CAP = 200;
|
|
20
|
+
/**
|
|
21
|
+
* The total number of nodes {@link boundSpec} will visit across one walk,
|
|
22
|
+
* regardless of how many times a shared reference recurs. Bounds the
|
|
23
|
+
* combinatorial work a DAG of shared or self-referential nodes would
|
|
24
|
+
* otherwise force even though each individual array stays within
|
|
25
|
+
* {@link CHILDREN_CAP}.
|
|
26
|
+
*/
|
|
27
|
+
const NODE_BUDGET = 5e3;
|
|
28
|
+
/** Renders the warning detail a protocol reports for one {@link ClampReason}. */
|
|
29
|
+
function clampReasonDetail(reason) {
|
|
30
|
+
if (reason === "budget") return `the tree was truncated after ${NODE_BUDGET} nodes.`;
|
|
31
|
+
if (reason === "cycle") return "a self-referencing node was dropped.";
|
|
32
|
+
if (reason === "depth") return `nodes deeper than 32 levels were dropped.`;
|
|
33
|
+
return `children were clamped to 200 entries.`;
|
|
34
|
+
}
|
|
35
|
+
function boundNode(value, depth, onClamp, state, ancestors) {
|
|
36
|
+
if (state.remaining <= 0) {
|
|
37
|
+
if (!state.exhausted) {
|
|
38
|
+
state.exhausted = true;
|
|
39
|
+
onClamp("budget");
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
state.remaining -= 1;
|
|
44
|
+
if (depth > 64) {
|
|
45
|
+
onClamp("depth");
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
if (ancestors.has(value)) {
|
|
50
|
+
onClamp("cycle");
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
ancestors.add(value);
|
|
54
|
+
const { items: bounded, truncated } = copyBounded(value, 200);
|
|
55
|
+
if (truncated) onClamp("children");
|
|
56
|
+
const result = bounded.map((item) => boundNode(item, depth + 1, onClamp, state, ancestors));
|
|
57
|
+
ancestors.delete(value);
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
if (value !== null && typeof value === "object" && "children" in value) {
|
|
61
|
+
if (ancestors.has(value)) {
|
|
62
|
+
onClamp("cycle");
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
ancestors.add(value);
|
|
66
|
+
const record = value;
|
|
67
|
+
const result = {
|
|
68
|
+
...record,
|
|
69
|
+
children: boundNode(record["children"], depth + 1, onClamp, state, ancestors)
|
|
70
|
+
};
|
|
71
|
+
ancestors.delete(value);
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Produces a bounded plain copy of a raw generative-ui spec before it
|
|
78
|
+
* reaches `normalizeSpec`, whose own traversal of the root array or any
|
|
79
|
+
* `children` array walks the full reported length of a hostile proxied
|
|
80
|
+
* array before any per-field cap downstream ever applies. Every array
|
|
81
|
+
* (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}
|
|
82
|
+
* entries by index, which bounds a proxy however it fabricates `length` or
|
|
83
|
+
* replaces its array methods; recursion itself is capped at
|
|
84
|
+
* {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was
|
|
85
|
+
* truncated, receiving the reason for that truncation: `"children"`,
|
|
86
|
+
* `"depth"`, `"budget"`, or `"cycle"`. The walk also spends a total budget of
|
|
87
|
+
* {@link NODE_BUDGET} nodes, so shared references cannot multiply work
|
|
88
|
+
* exponentially, and a node that is its own ancestor is cut to `null`.
|
|
89
|
+
*/
|
|
90
|
+
function boundSpec(spec, onClamp) {
|
|
91
|
+
return boundNode(spec, 0, onClamp, {
|
|
92
|
+
remaining: NODE_BUDGET,
|
|
93
|
+
exhausted: false
|
|
94
|
+
}, /* @__PURE__ */ new WeakSet());
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { CHILDREN_CAP, MAX_ELEMENT_DEPTH, MAX_TRAVERSAL_DEPTH, NODE_BUDGET, boundSpec, clampReasonDetail };
|
|
98
|
+
|
|
99
|
+
//# sourceMappingURL=boundSpec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundSpec.js","names":[],"sources":["../../src/convert/boundSpec.ts"],"sourcesContent":["import { copyBounded } from \"./copyBounded\";\n\n/** The maximum traversal depth, counted in visited values, not tree levels. */\nexport const MAX_TRAVERSAL_DEPTH = 64;\n\n/**\n * The element-nesting ceiling a caller observes. `boundNode` visits an element\n * and its `children` array separately, so one level of nesting spends two of\n * {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never\n * the looser of the two. A root array costs one more level than a root object.\n */\nexport const MAX_ELEMENT_DEPTH = MAX_TRAVERSAL_DEPTH / 2;\n\n/**\n * The maximum number of entries kept at any single level (root, or a\n * `children` array at any depth) of the raw spec, before it ever reaches\n * `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass\n * can never be made to walk further than this regardless of what the array\n * claims about itself.\n */\nexport const CHILDREN_CAP = 200;\n\n/**\n * The total number of nodes {@link boundSpec} will visit across one walk,\n * regardless of how many times a shared reference recurs. Bounds the\n * combinatorial work a DAG of shared or self-referential nodes would\n * otherwise force even though each individual array stays within\n * {@link CHILDREN_CAP}.\n */\nexport const NODE_BUDGET = 5000;\n\nexport type ClampReason = \"children\" | \"budget\" | \"cycle\" | \"depth\";\n\n/** Renders the warning detail a protocol reports for one {@link ClampReason}. */\nexport function clampReasonDetail(reason: ClampReason): string {\n if (reason === \"budget\") {\n return `the tree was truncated after ${NODE_BUDGET} nodes.`;\n }\n if (reason === \"cycle\") return \"a self-referencing node was dropped.\";\n if (reason === \"depth\") {\n return `nodes deeper than ${MAX_ELEMENT_DEPTH} levels were dropped.`;\n }\n return `children were clamped to ${CHILDREN_CAP} entries.`;\n}\n\ninterface BoundState {\n remaining: number;\n exhausted: boolean;\n}\n\nfunction boundNode(\n value: unknown,\n depth: number,\n onClamp: (reason: ClampReason) => void,\n state: BoundState,\n ancestors: WeakSet<object>,\n): unknown {\n if (state.remaining <= 0) {\n if (!state.exhausted) {\n state.exhausted = true;\n onClamp(\"budget\");\n }\n return null;\n }\n state.remaining -= 1;\n if (depth > MAX_TRAVERSAL_DEPTH) {\n onClamp(\"depth\");\n return null;\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const { items: bounded, truncated } = copyBounded(value, CHILDREN_CAP);\n if (truncated) onClamp(\"children\");\n const result = bounded.map((item) =>\n boundNode(item, depth + 1, onClamp, state, ancestors),\n );\n ancestors.delete(value);\n return result;\n }\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"children\" in (value as Record<string, unknown>)\n ) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const record = value as Record<string, unknown>;\n const result = {\n ...record,\n children: boundNode(\n record[\"children\"],\n depth + 1,\n onClamp,\n state,\n ancestors,\n ),\n };\n ancestors.delete(value);\n return result;\n }\n return value;\n}\n\n/**\n * Produces a bounded plain copy of a raw generative-ui spec before it\n * reaches `normalizeSpec`, whose own traversal of the root array or any\n * `children` array walks the full reported length of a hostile proxied\n * array before any per-field cap downstream ever applies. Every array\n * (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}\n * entries by index, which bounds a proxy however it fabricates `length` or\n * replaces its array methods; recursion itself is capped at\n * {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was\n * truncated, receiving the reason for that truncation: `\"children\"`,\n * `\"depth\"`, `\"budget\"`, or `\"cycle\"`. The walk also spends a total budget of\n * {@link NODE_BUDGET} nodes, so shared references cannot multiply work\n * exponentially, and a node that is its own ancestor is cut to `null`.\n */\nexport function boundSpec(\n spec: unknown,\n onClamp: (reason: ClampReason) => void,\n): unknown {\n return boundNode(\n spec,\n 0,\n onClamp,\n { remaining: NODE_BUDGET, exhausted: false },\n new WeakSet(),\n );\n}\n"],"mappings":";;;AAGA,MAAa,sBAAsB;;;;;;;AAQnC,MAAa,oBAAA;;;;;;;;AASb,MAAa,eAAe;;;;;;;;AAS5B,MAAa,cAAc;;AAK3B,SAAgB,kBAAkB,QAA6B;CAC7D,IAAI,WAAW,UACb,OAAO,gCAAgC,YAAY;CAErD,IAAI,WAAW,SAAS,OAAO;CAC/B,IAAI,WAAW,SACb,OAAO;CAET,OAAO;AACT;AAOA,SAAS,UACP,OACA,OACA,SACA,OACA,WACS;CACT,IAAI,MAAM,aAAa,GAAG;EACxB,IAAI,CAAC,MAAM,WAAW;GACpB,MAAM,YAAY;GAClB,QAAQ,QAAQ;EAClB;EACA,OAAO;CACT;CACA,MAAM,aAAa;CACnB,IAAI,QAAA,IAA6B;EAC/B,QAAQ,OAAO;EACf,OAAO;CACT;CACA,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,IAAI,UAAU,IAAI,KAAK,GAAG;GACxB,QAAQ,OAAO;GACf,OAAO;EACT;EACA,UAAU,IAAI,KAAK;EACnB,MAAM,EAAE,OAAO,SAAS,cAAc,YAAY,OAAA,GAAmB;EACrE,IAAI,WAAW,QAAQ,UAAU;EACjC,MAAM,SAAS,QAAQ,KAAK,SAC1B,UAAU,MAAM,QAAQ,GAAG,SAAS,OAAO,SAAS,CACtD;EACA,UAAU,OAAO,KAAK;EACtB,OAAO;CACT;CACA,IACE,UAAU,QACV,OAAO,UAAU,YACjB,cAAe,OACf;EACA,IAAI,UAAU,IAAI,KAAK,GAAG;GACxB,QAAQ,OAAO;GACf,OAAO;EACT;EACA,UAAU,IAAI,KAAK;EACnB,MAAM,SAAS;EACf,MAAM,SAAS;GACb,GAAG;GACH,UAAU,UACR,OAAO,aACP,QAAQ,GACR,SACA,OACA,SACF;EACF;EACA,UAAU,OAAO,KAAK;EACtB,OAAO;CACT;CACA,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,SAAgB,UACd,MACA,SACS;CACT,OAAO,UACL,MACA,GACA,SACA;EAAE,WAAW;EAAa,WAAW;CAAM,mBAC3C,IAAI,QAAQ,CACd;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/convert/copyBounded.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Copies at most `cap` entries out of `value` by index.
|
|
4
|
+
*
|
|
5
|
+
* Every array method dispatches on the input: a replaced `slice` is called
|
|
6
|
+
* directly, and the intrinsic `Array.prototype.slice.call` still reaches a
|
|
7
|
+
* `constructor` with a `@@species` whose `[[Construct]]` returns an arbitrary
|
|
8
|
+
* object, which then supplies the `map` or `some` the caller runs next. Reading
|
|
9
|
+
* indices consults neither, so it is the only form that bounds a hostile array.
|
|
10
|
+
*
|
|
11
|
+
* Absent indices stay absent, matching what `slice` produces for a sparse
|
|
12
|
+
* input, so callers that skip holes still skip them. `truncated` comes from the
|
|
13
|
+
* same `length` read as the bound, so an input cannot report one figure to the
|
|
14
|
+
* cap and another to the caller's warning.
|
|
15
|
+
*
|
|
16
|
+
* A reported `length` is normalized the way `ToLength` does before it reaches
|
|
17
|
+
* `count`, because a fractional one would otherwise reach `items.length` and
|
|
18
|
+
* throw `RangeError` where `slice` returns a bounded result. `truncated` reads
|
|
19
|
+
* that same normalized figure, so a `length` whose `valueOf` answers differently
|
|
20
|
+
* on a second conversion cannot separate the warning from the bound.
|
|
21
|
+
*/
|
|
22
|
+
declare const copyBounded: <T>(value: readonly T[], cap: number) => {
|
|
23
|
+
readonly items: T[];
|
|
24
|
+
readonly truncated: boolean;
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { copyBounded };
|
|
28
|
+
//# sourceMappingURL=copyBounded.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copyBounded.d.ts","names":[],"sources":["../../src/convert/copyBounded.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;cAoBa,cAAe,GAC1B,gBAAgB,KAChB;WACY,OAAO;WAAc"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/convert/copyBounded.ts
|
|
2
|
+
/**
|
|
3
|
+
* Copies at most `cap` entries out of `value` by index.
|
|
4
|
+
*
|
|
5
|
+
* Every array method dispatches on the input: a replaced `slice` is called
|
|
6
|
+
* directly, and the intrinsic `Array.prototype.slice.call` still reaches a
|
|
7
|
+
* `constructor` with a `@@species` whose `[[Construct]]` returns an arbitrary
|
|
8
|
+
* object, which then supplies the `map` or `some` the caller runs next. Reading
|
|
9
|
+
* indices consults neither, so it is the only form that bounds a hostile array.
|
|
10
|
+
*
|
|
11
|
+
* Absent indices stay absent, matching what `slice` produces for a sparse
|
|
12
|
+
* input, so callers that skip holes still skip them. `truncated` comes from the
|
|
13
|
+
* same `length` read as the bound, so an input cannot report one figure to the
|
|
14
|
+
* cap and another to the caller's warning.
|
|
15
|
+
*
|
|
16
|
+
* A reported `length` is normalized the way `ToLength` does before it reaches
|
|
17
|
+
* `count`, because a fractional one would otherwise reach `items.length` and
|
|
18
|
+
* throw `RangeError` where `slice` returns a bounded result. `truncated` reads
|
|
19
|
+
* that same normalized figure, so a `length` whose `valueOf` answers differently
|
|
20
|
+
* on a second conversion cannot separate the warning from the bound.
|
|
21
|
+
*/
|
|
22
|
+
const copyBounded = (value, cap) => {
|
|
23
|
+
const reported = value.length;
|
|
24
|
+
const integral = Math.trunc(Number(reported));
|
|
25
|
+
const usable = Number.isNaN(integral) || integral <= 0 ? 0 : Math.min(integral, Number.MAX_SAFE_INTEGER);
|
|
26
|
+
const count = Math.min(cap, usable);
|
|
27
|
+
const items = [];
|
|
28
|
+
for (let index = 0; index < count; index += 1) if (index in value) items[index] = value[index];
|
|
29
|
+
items.length = count;
|
|
30
|
+
return {
|
|
31
|
+
items,
|
|
32
|
+
truncated: usable > cap
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { copyBounded };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=copyBounded.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copyBounded.js","names":[],"sources":["../../src/convert/copyBounded.ts"],"sourcesContent":["/**\n * Copies at most `cap` entries out of `value` by index.\n *\n * Every array method dispatches on the input: a replaced `slice` is called\n * directly, and the intrinsic `Array.prototype.slice.call` still reaches a\n * `constructor` with a `@@species` whose `[[Construct]]` returns an arbitrary\n * object, which then supplies the `map` or `some` the caller runs next. Reading\n * indices consults neither, so it is the only form that bounds a hostile array.\n *\n * Absent indices stay absent, matching what `slice` produces for a sparse\n * input, so callers that skip holes still skip them. `truncated` comes from the\n * same `length` read as the bound, so an input cannot report one figure to the\n * cap and another to the caller's warning.\n *\n * A reported `length` is normalized the way `ToLength` does before it reaches\n * `count`, because a fractional one would otherwise reach `items.length` and\n * throw `RangeError` where `slice` returns a bounded result. `truncated` reads\n * that same normalized figure, so a `length` whose `valueOf` answers differently\n * on a second conversion cannot separate the warning from the bound.\n */\nexport const copyBounded = <T>(\n value: readonly T[],\n cap: number,\n): { readonly items: T[]; readonly truncated: boolean } => {\n const reported = value.length;\n const integral = Math.trunc(Number(reported));\n const usable =\n Number.isNaN(integral) || integral <= 0\n ? 0\n : Math.min(integral, Number.MAX_SAFE_INTEGER);\n const count = Math.min(cap, usable);\n const items: T[] = [];\n for (let index = 0; index < count; index += 1) {\n if (index in value) items[index] = value[index] as T;\n }\n items.length = count;\n return { items, truncated: usable > cap };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,eACX,OACA,QACyD;CACzD,MAAM,WAAW,MAAM;CACvB,MAAM,WAAW,KAAK,MAAM,OAAO,QAAQ,CAAC;CAC5C,MAAM,SACJ,OAAO,MAAM,QAAQ,KAAK,YAAY,IAClC,IACA,KAAK,IAAI,UAAU,OAAO,gBAAgB;CAChD,MAAM,QAAQ,KAAK,IAAI,KAAK,MAAM;CAClC,MAAM,QAAa,CAAC;CACpB,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,SAAS,GAC1C,IAAI,SAAS,OAAO,MAAM,SAAS,MAAM;CAE3C,MAAM,SAAS;CACf,OAAO;EAAE;EAAO,WAAW,SAAS;CAAI;AAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isElement.d.ts","names":[],"sources":["../../src/convert/isElement.ts"],"mappings":";;cAEa,YACX,MAAM,qBACL,QAAQ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isElement.js","names":[],"sources":["../../src/convert/isElement.ts"],"sourcesContent":["import type { NormalizedUIElement, NormalizedUINode } from \"../ir\";\n\nexport const isElement = (\n node: NormalizedUINode,\n): node is NormalizedUIElement =>\n typeof node === \"object\" && node !== null && !Array.isArray(node);\n"],"mappings":";AAEA,MAAa,aACX,SAEA,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NormalizedUIElement, NormalizedUINode } from "../ir.js";
|
|
2
|
+
//#region src/convert/takeRun.d.ts
|
|
3
|
+
declare function takeRun(nodes: readonly (NormalizedUINode | undefined)[], index: number, matches: (element: NormalizedUIElement) => boolean): {
|
|
4
|
+
run: NormalizedUIElement[];
|
|
5
|
+
next: number;
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { takeRun };
|
|
9
|
+
//# sourceMappingURL=takeRun.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"takeRun.d.ts","names":[],"sources":["../../src/convert/takeRun.ts"],"mappings":";;iBAGgB,QACd,iBAAiB,iCACjB,eACA,UAAU,SAAS;EAChB,KAAK;EAAuB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isElement } from "./isElement.js";
|
|
2
|
+
//#region src/convert/takeRun.ts
|
|
3
|
+
function takeRun(nodes, index, matches) {
|
|
4
|
+
const run = [];
|
|
5
|
+
let next = index;
|
|
6
|
+
while (next < nodes.length) {
|
|
7
|
+
const candidate = nodes[next];
|
|
8
|
+
if (candidate === void 0 || !isElement(candidate) || !matches(candidate)) break;
|
|
9
|
+
run.push(candidate);
|
|
10
|
+
next += 1;
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
run,
|
|
14
|
+
next
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { takeRun };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=takeRun.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"takeRun.js","names":[],"sources":["../../src/convert/takeRun.ts"],"sourcesContent":["import type { NormalizedUIElement, NormalizedUINode } from \"../ir\";\nimport { isElement } from \"./isElement\";\n\nexport function takeRun(\n nodes: readonly (NormalizedUINode | undefined)[],\n index: number,\n matches: (element: NormalizedUIElement) => boolean,\n): { run: NormalizedUIElement[]; next: number } {\n const run: NormalizedUIElement[] = [];\n let next = index;\n while (next < nodes.length) {\n const candidate = nodes[next];\n if (candidate === undefined || !isElement(candidate) || !matches(candidate))\n break;\n run.push(candidate);\n next += 1;\n }\n return { run, next };\n}\n"],"mappings":";;AAGA,SAAgB,QACd,OACA,OACA,SAC8C;CAC9C,MAAM,MAA6B,CAAC;CACpC,IAAI,OAAO;CACX,OAAO,OAAO,MAAM,QAAQ;EAC1B,MAAM,YAAY,MAAM;EACxB,IAAI,cAAc,KAAA,KAAa,CAAC,UAAU,SAAS,KAAK,CAAC,QAAQ,SAAS,GACxE;EACF,IAAI,KAAK,SAAS;EAClB,QAAQ;CACV;CACA,OAAO;EAAE;EAAK;CAAK;AACrB"}
|
|
@@ -1,30 +1,5 @@
|
|
|
1
1
|
import { SlackAlertBlock, SlackButtonElement, SlackCardBlock, SlackCarouselBlock, SlackContextBlock, SlackDataTableBlock, SlackDataTableCell, SlackImageBlock, SlackSectionBlock, SlackTextObject } from "./types.js";
|
|
2
2
|
//#region src/slack/constants.d.ts
|
|
3
|
-
/** The maximum traversal depth, counted in visited values, not tree levels. */
|
|
4
|
-
declare const MAX_TRAVERSAL_DEPTH = 64;
|
|
5
|
-
/**
|
|
6
|
-
* The element-nesting ceiling a caller observes. `boundNode` visits an element
|
|
7
|
-
* and its `children` array separately, so one level of nesting spends two of
|
|
8
|
-
* {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never
|
|
9
|
-
* the looser of the two. A root array costs one more level than a root object.
|
|
10
|
-
*/
|
|
11
|
-
declare const MAX_ELEMENT_DEPTH: number;
|
|
12
|
-
/**
|
|
13
|
-
* The maximum number of entries kept at any single level (root, or a
|
|
14
|
-
* `children` array at any depth) of the raw spec, before it ever reaches
|
|
15
|
-
* `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
|
|
16
|
-
* can never be made to walk further than this regardless of what the array
|
|
17
|
-
* claims about itself.
|
|
18
|
-
*/
|
|
19
|
-
declare const CHILDREN_CAP = 200;
|
|
20
|
-
/**
|
|
21
|
-
* The total number of nodes the pre-normalization bounding walk will visit
|
|
22
|
-
* across one call, regardless of how many times a shared reference recurs.
|
|
23
|
-
* Bounds the combinatorial work a DAG of shared or self-referential nodes
|
|
24
|
-
* would otherwise force even though each individual array stays within
|
|
25
|
-
* {@link CHILDREN_CAP}.
|
|
26
|
-
*/
|
|
27
|
-
declare const NODE_BUDGET = 5000;
|
|
28
3
|
/** The block limit for a Slack message. */
|
|
29
4
|
declare const MESSAGE_BLOCK_CAP = 50;
|
|
30
5
|
/** The block limit for a Slack modal. */
|
|
@@ -114,5 +89,5 @@ declare function buildDataTableBlock(rows: readonly (readonly SlackDataTableCell
|
|
|
114
89
|
/** Builds the final note used when a surface block budget is exceeded. */
|
|
115
90
|
declare function buildBlockBudgetNote(omitted: number): SlackContextBlock;
|
|
116
91
|
//#endregion
|
|
117
|
-
export { ACTIONS_ELEMENT_CAP, ACTION_ID_CAP, ALERT_TEXT_CAP, BUTTON_VALUE_CAP, CARD_ACTIONS_CAP, CARD_BODY_CAP, CARD_SUBTEXT_CAP, CARD_TITLE_CAP, CAROUSEL_CARD_CAP, CAROUSEL_CARD_MIN,
|
|
92
|
+
export { ACTIONS_ELEMENT_CAP, ACTION_ID_CAP, ALERT_TEXT_CAP, BUTTON_VALUE_CAP, CARD_ACTIONS_CAP, CARD_BODY_CAP, CARD_SUBTEXT_CAP, CARD_TITLE_CAP, CAROUSEL_CARD_CAP, CAROUSEL_CARD_MIN, CONTEXT_ELEMENT_CAP, CONTEXT_TEXT_CAP, DATA_TABLE_CHAR_BUDGET, DATA_TABLE_COLUMN_CAP, DATA_TABLE_ROW_CAP, FACT_FIELD_CAP, FACT_FIELD_TEXT_CAP, HEADER_TEXT_CAP, INBOUND_BLOCK_CAP, INPUT_LABEL_CAP, INTERACTIVE_TEXT_CAP, MARKDOWN_TEXT_BUDGET, MESSAGE_BLOCK_CAP, MODAL_BLOCK_CAP, PLACEHOLDER_TEXT_CAP, RADIO_OPTION_CAP, SECTION_TEXT_CAP, SELECT_OPTION_CAP, TABLE_CAPTION, buildAlertBlock, buildAlertFallback, buildBlockBudgetNote, buildCardBlock, buildCarouselBlock, buildDataTableBlock };
|
|
118
93
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","names":[],"sources":["../../src/slack/constants.ts"],"mappings":";;;cAca
|
|
1
|
+
{"version":3,"file":"constants.d.ts","names":[],"sources":["../../src/slack/constants.ts"],"mappings":";;;cAca;;cAGA;;;;;;cAOA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;;;;cAMA;;cAGA;;;;;cAMA;;cAGA;;cAGA;;iBAGG,mBACd,eACA,qBACA,4DACW,mBAAmB;;iBAmBhB,gBACd,cACA,kDACC;KAKE;WACM,YAAY;WACZ,QAAQ;WACR,WAAW;WACX,OAAO;WACP,UAAU;WACV,mBAAmB;;;iBAId,eAAe,QAAQ,kBAAkB;;iBAczC,mBACd,mBAAmB,mBAClB;;iBAKa,oBACd,yBAAyB,0BACxB;;iBAKa,qBAAqB,kBAAkB"}
|
package/dist/slack/constants.js
CHANGED
|
@@ -1,29 +1,4 @@
|
|
|
1
1
|
//#region src/slack/constants.ts
|
|
2
|
-
/** The maximum traversal depth, counted in visited values, not tree levels. */
|
|
3
|
-
const MAX_TRAVERSAL_DEPTH = 64;
|
|
4
|
-
/**
|
|
5
|
-
* The element-nesting ceiling a caller observes. `boundNode` visits an element
|
|
6
|
-
* and its `children` array separately, so one level of nesting spends two of
|
|
7
|
-
* {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never
|
|
8
|
-
* the looser of the two. A root array costs one more level than a root object.
|
|
9
|
-
*/
|
|
10
|
-
const MAX_ELEMENT_DEPTH = 32;
|
|
11
|
-
/**
|
|
12
|
-
* The maximum number of entries kept at any single level (root, or a
|
|
13
|
-
* `children` array at any depth) of the raw spec, before it ever reaches
|
|
14
|
-
* `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
|
|
15
|
-
* can never be made to walk further than this regardless of what the array
|
|
16
|
-
* claims about itself.
|
|
17
|
-
*/
|
|
18
|
-
const CHILDREN_CAP = 200;
|
|
19
|
-
/**
|
|
20
|
-
* The total number of nodes the pre-normalization bounding walk will visit
|
|
21
|
-
* across one call, regardless of how many times a shared reference recurs.
|
|
22
|
-
* Bounds the combinatorial work a DAG of shared or self-referential nodes
|
|
23
|
-
* would otherwise force even though each individual array stays within
|
|
24
|
-
* {@link CHILDREN_CAP}.
|
|
25
|
-
*/
|
|
26
|
-
const NODE_BUDGET = 5e3;
|
|
27
2
|
/** The block limit for a Slack message. */
|
|
28
3
|
const MESSAGE_BLOCK_CAP = 50;
|
|
29
4
|
/** The block limit for a Slack modal. */
|
|
@@ -164,6 +139,6 @@ function buildBlockBudgetNote(omitted) {
|
|
|
164
139
|
};
|
|
165
140
|
}
|
|
166
141
|
//#endregion
|
|
167
|
-
export { ACTIONS_ELEMENT_CAP, ACTION_ID_CAP, ALERT_TEXT_CAP, BUTTON_VALUE_CAP, CARD_ACTIONS_CAP, CARD_BODY_CAP, CARD_SUBTEXT_CAP, CARD_TITLE_CAP, CAROUSEL_CARD_CAP, CAROUSEL_CARD_MIN,
|
|
142
|
+
export { ACTIONS_ELEMENT_CAP, ACTION_ID_CAP, ALERT_TEXT_CAP, BUTTON_VALUE_CAP, CARD_ACTIONS_CAP, CARD_BODY_CAP, CARD_SUBTEXT_CAP, CARD_TITLE_CAP, CAROUSEL_CARD_CAP, CAROUSEL_CARD_MIN, CONTEXT_ELEMENT_CAP, CONTEXT_TEXT_CAP, DATA_TABLE_CHAR_BUDGET, DATA_TABLE_COLUMN_CAP, DATA_TABLE_ROW_CAP, FACT_FIELD_CAP, FACT_FIELD_TEXT_CAP, HEADER_TEXT_CAP, INBOUND_BLOCK_CAP, INPUT_LABEL_CAP, INTERACTIVE_TEXT_CAP, MARKDOWN_TEXT_BUDGET, MESSAGE_BLOCK_CAP, MODAL_BLOCK_CAP, PLACEHOLDER_TEXT_CAP, RADIO_OPTION_CAP, SECTION_TEXT_CAP, SELECT_OPTION_CAP, TABLE_CAPTION, buildAlertBlock, buildAlertFallback, buildBlockBudgetNote, buildCardBlock, buildCarouselBlock, buildDataTableBlock };
|
|
168
143
|
|
|
169
144
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":[],"sources":["../../src/slack/constants.ts"],"sourcesContent":["import type {\n SlackAlertBlock,\n SlackButtonElement,\n SlackCardBlock,\n SlackCarouselBlock,\n SlackContextBlock,\n SlackDataTableBlock,\n SlackDataTableCell,\n SlackImageBlock,\n SlackSectionBlock,\n SlackTextObject,\n} from \"./types\";\n\n/** The
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../src/slack/constants.ts"],"sourcesContent":["import type {\n SlackAlertBlock,\n SlackButtonElement,\n SlackCardBlock,\n SlackCarouselBlock,\n SlackContextBlock,\n SlackDataTableBlock,\n SlackDataTableCell,\n SlackImageBlock,\n SlackSectionBlock,\n SlackTextObject,\n} from \"./types\";\n\n/** The block limit for a Slack message. */\nexport const MESSAGE_BLOCK_CAP = 50;\n\n/** The block limit for a Slack modal. */\nexport const MODAL_BLOCK_CAP = 100;\n\n/**\n * The maximum number of top-level blocks accepted when parsing Slack Block\n * Kit JSON back into generative UI, guarding against a hostile or malformed\n * payload.\n */\nexport const INBOUND_BLOCK_CAP = 200;\n\n/** The character limit for header text. */\nexport const HEADER_TEXT_CAP = 150;\n\n/** The character limit for section text. */\nexport const SECTION_TEXT_CAP = 3000;\n\n/** The maximum number of fields in one section. */\nexport const FACT_FIELD_CAP = 10;\n\n/** The character limit for one section field. */\nexport const FACT_FIELD_TEXT_CAP = 2000;\n\n/** The character limit for button labels and option labels. */\nexport const INTERACTIVE_TEXT_CAP = 75;\n\n/** The character limit for an action_id. */\nexport const ACTION_ID_CAP = 255;\n\n/** The character limit for a button's serialized value payload. */\nexport const BUTTON_VALUE_CAP = 2000;\n\n/** The maximum number of elements in one actions block. */\nexport const ACTIONS_ELEMENT_CAP = 25;\n\n/** The maximum number of options in a static select. */\nexport const SELECT_OPTION_CAP = 100;\n\n/** The maximum number of options in a radio-button group. */\nexport const RADIO_OPTION_CAP = 10;\n\n/** The character limit for a select or input placeholder. */\nexport const PLACEHOLDER_TEXT_CAP = 150;\n\n/** The character limit for an input's label. */\nexport const INPUT_LABEL_CAP = 2000;\n\n/** The maximum number of elements in one context block. */\nexport const CONTEXT_ELEMENT_CAP = 10;\n\n/** The character limit for one context element's text. */\nexport const CONTEXT_TEXT_CAP = 3000;\n\n/** The character limit for card titles and subtitles. */\nexport const CARD_TITLE_CAP = 150;\n\n/** The character limit for a card's body text. */\nexport const CARD_BODY_CAP = 200;\n\n/** The character limit for a card's subtext. */\nexport const CARD_SUBTEXT_CAP = 200;\n\n/** The maximum number of buttons in a card's actions. */\nexport const CARD_ACTIONS_CAP = 3;\n\n/** The minimum number of elements in a renderable carousel. */\nexport const CAROUSEL_CARD_MIN = 1;\n\n/** The maximum number of elements in a carousel. */\nexport const CAROUSEL_CARD_CAP = 10;\n\n/** The character limit for an alert's text. */\nexport const ALERT_TEXT_CAP = 200;\n\n/**\n * The maximum number of data rows in a data-table block; the emitted `rows`\n * array additionally carries one header row.\n */\nexport const DATA_TABLE_ROW_CAP = 200;\n\n/** The maximum number of columns in a data-table block. */\nexport const DATA_TABLE_COLUMN_CAP = 20;\n\n/**\n * The cumulative character limit (summed across every cell's text) shared by\n * every data-table block in one payload.\n */\nexport const DATA_TABLE_CHAR_BUDGET = 20000;\n\n/** The caption emitted on every native data-table block. */\nexport const TABLE_CAPTION = \"Table\";\n\n/** The cumulative character limit for native markdown blocks. */\nexport const MARKDOWN_TEXT_BUDGET = 12000;\n\n/** Builds the message-surface fallback for an alert. */\nexport function buildAlertFallback(\n title: string,\n description: string,\n tone: \"info\" | \"success\" | \"warning\" | \"danger\",\n): readonly [SlackContextBlock, SlackSectionBlock] {\n const emoji = {\n info: \"ℹ️\",\n success: \"✅\",\n warning: \"⚠️\",\n danger: \"🛑\",\n }[tone];\n return [\n {\n type: \"context\",\n elements: [\n { type: \"mrkdwn\", text: title ? `${emoji} *${title}*` : emoji },\n ],\n },\n { type: \"section\", text: { type: \"mrkdwn\", text: description } },\n ];\n}\n\n/** Builds a 2026 Slack modal-only alert block, mapping `tone` to its `level`. */\nexport function buildAlertBlock(\n text: string,\n tone: \"info\" | \"success\" | \"warning\" | \"danger\",\n): SlackAlertBlock {\n const level = tone === \"danger\" ? \"error\" : tone;\n return { type: \"alert\", text: { type: \"mrkdwn\", text }, level };\n}\n\ntype CardBlockFields = {\n readonly heroImage?: SlackImageBlock;\n readonly title?: SlackTextObject;\n readonly subtitle?: SlackTextObject;\n readonly body?: SlackTextObject;\n readonly subtext?: SlackTextObject;\n readonly actions?: readonly SlackButtonElement[];\n};\n\n/** Builds a 2026 Slack card block from its already-clamped fields. */\nexport function buildCardBlock(fields: CardBlockFields): SlackCardBlock {\n const { heroImage, title, subtitle, body, subtext, actions } = fields;\n return {\n type: \"card\",\n ...(heroImage !== undefined ? { hero_image: heroImage } : {}),\n ...(title !== undefined ? { title } : {}),\n ...(subtitle !== undefined ? { subtitle } : {}),\n ...(body !== undefined ? { body } : {}),\n ...(subtext !== undefined ? { subtext } : {}),\n ...(actions !== undefined ? { actions } : {}),\n };\n}\n\n/** Builds a 2026 Slack carousel block. */\nexport function buildCarouselBlock(\n elements: readonly SlackCardBlock[],\n): SlackCarouselBlock {\n return { type: \"carousel\", elements };\n}\n\n/** Builds a 2026 Slack data-table block; `rows[0]` must be the header row. */\nexport function buildDataTableBlock(\n rows: readonly (readonly SlackDataTableCell[])[],\n): SlackDataTableBlock {\n return { type: \"data_table\", caption: TABLE_CAPTION, rows };\n}\n\n/** Builds the final note used when a surface block budget is exceeded. */\nexport function buildBlockBudgetNote(omitted: number): SlackContextBlock {\n return {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: `${omitted} ${omitted === 1 ? \"block was\" : \"blocks were\"} omitted.`,\n },\n ],\n };\n}\n"],"mappings":";;AAcA,MAAa,oBAAoB;;AAGjC,MAAa,kBAAkB;;;;;;AAO/B,MAAa,oBAAoB;;AAGjC,MAAa,kBAAkB;;AAG/B,MAAa,mBAAmB;;AAGhC,MAAa,iBAAiB;;AAG9B,MAAa,sBAAsB;;AAGnC,MAAa,uBAAuB;;AAGpC,MAAa,gBAAgB;;AAG7B,MAAa,mBAAmB;;AAGhC,MAAa,sBAAsB;;AAGnC,MAAa,oBAAoB;;AAGjC,MAAa,mBAAmB;;AAGhC,MAAa,uBAAuB;;AAGpC,MAAa,kBAAkB;;AAG/B,MAAa,sBAAsB;;AAGnC,MAAa,mBAAmB;;AAGhC,MAAa,iBAAiB;;AAG9B,MAAa,gBAAgB;;AAG7B,MAAa,mBAAmB;;AAGhC,MAAa,mBAAmB;;AAGhC,MAAa,oBAAoB;;AAGjC,MAAa,oBAAoB;;AAGjC,MAAa,iBAAiB;;;;;AAM9B,MAAa,qBAAqB;;AAGlC,MAAa,wBAAwB;;;;;AAMrC,MAAa,yBAAyB;;AAGtC,MAAa,gBAAgB;;AAG7B,MAAa,uBAAuB;;AAGpC,SAAgB,mBACd,OACA,aACA,MACiD;CACjD,MAAM,QAAQ;EACZ,MAAM;EACN,SAAS;EACT,SAAS;EACT,QAAQ;CACV,EAAE;CACF,OAAO,CACL;EACE,MAAM;EACN,UAAU,CACR;GAAE,MAAM;GAAU,MAAM,QAAQ,GAAG,MAAM,IAAI,MAAM,KAAK;EAAM,CAChE;CACF,GACA;EAAE,MAAM;EAAW,MAAM;GAAE,MAAM;GAAU,MAAM;EAAY;CAAE,CACjE;AACF;;AAGA,SAAgB,gBACd,MACA,MACiB;CAEjB,OAAO;EAAE,MAAM;EAAS,MAAM;GAAE,MAAM;GAAU;EAAK;EAAG,OAD1C,SAAS,WAAW,UAAU;CACkB;AAChE;;AAYA,SAAgB,eAAe,QAAyC;CACtE,MAAM,EAAE,WAAW,OAAO,UAAU,MAAM,SAAS,YAAY;CAC/D,OAAO;EACL,MAAM;EACN,GAAI,cAAc,KAAA,IAAY,EAAE,YAAY,UAAU,IAAI,CAAC;EAC3D,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;EACvC,GAAI,aAAa,KAAA,IAAY,EAAE,SAAS,IAAI,CAAC;EAC7C,GAAI,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;EACrC,GAAI,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;EAC3C,GAAI,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;CAC7C;AACF;;AAGA,SAAgB,mBACd,UACoB;CACpB,OAAO;EAAE,MAAM;EAAY;CAAS;AACtC;;AAGA,SAAgB,oBACd,MACqB;CACrB,OAAO;EAAE,MAAM;EAAc,SAAS;EAAe;CAAK;AAC5D;;AAGA,SAAgB,qBAAqB,SAAoC;CACvE,OAAO;EACL,MAAM;EACN,UAAU,CACR;GACE,MAAM;GACN,MAAM,GAAG,QAAQ,GAAG,YAAY,IAAI,cAAc,cAAc;EAClE,CACF;CACF;AACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { copyBounded } from "../convert/copyBounded.js";
|
|
1
2
|
import "./constants.js";
|
|
2
3
|
//#region src/slack/fromSlackBlocks.ts
|
|
3
4
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -12,21 +13,16 @@ const warn = (warnings, code, component, detail) => {
|
|
|
12
13
|
});
|
|
13
14
|
};
|
|
14
15
|
/**
|
|
15
|
-
* The shared bounded-iteration primitive:
|
|
16
|
-
* without ever reading past that many indices, so a hostile array
|
|
17
|
-
* proxied with a fabricated `length`) cannot stall the event loop.
|
|
18
|
-
* itself performs the bounded read; only its own reported length is
|
|
19
|
-
* inspected to detect truncation.
|
|
16
|
+
* The shared bounded-iteration primitive: copies `value` to at most `cap`
|
|
17
|
+
* entries without ever reading past that many indices, so a hostile array
|
|
18
|
+
* (sparse or proxied with a fabricated `length`) cannot stall the event loop.
|
|
20
19
|
*/
|
|
21
20
|
const clampArray = (value, cap) => {
|
|
22
21
|
if (!Array.isArray(value)) return {
|
|
23
22
|
items: [],
|
|
24
23
|
truncated: false
|
|
25
24
|
};
|
|
26
|
-
return
|
|
27
|
-
items: value.slice(0, cap),
|
|
28
|
-
truncated: value.length > cap
|
|
29
|
-
};
|
|
25
|
+
return copyBounded(value, cap);
|
|
30
26
|
};
|
|
31
27
|
const boundedArray = (value, cap, warnings, component, detail) => {
|
|
32
28
|
const { items, truncated } = clampArray(value, cap);
|
|
@@ -157,7 +153,7 @@ const checkboxFrom = (element) => {
|
|
|
157
153
|
const first = (Array.isArray(element["options"]) ? element["options"] : [])[0];
|
|
158
154
|
const initialOptions = Array.isArray(element["initial_options"]) ? element["initial_options"] : [];
|
|
159
155
|
const firstValue = isRecord(first) && typeof first["value"] === "string" ? first["value"] : "";
|
|
160
|
-
const firstChecked = initialOptions
|
|
156
|
+
const firstChecked = copyBounded(initialOptions, 100).items.some((option) => isRecord(option) && option["value"] === firstValue);
|
|
161
157
|
return {
|
|
162
158
|
$type: "Checkbox",
|
|
163
159
|
label: isRecord(first) ? textOf(first["text"]) : "",
|