@tightknitai/slack-block-kit-validator 0.1.0-alpha.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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +186 -0
  3. package/dist/helpers/check-card-actions-max.d.ts +16 -0
  4. package/dist/helpers/check-card-actions-max.d.ts.map +1 -0
  5. package/dist/helpers/check-card-actions-max.js +25 -0
  6. package/dist/helpers/check-card-actions-max.js.map +1 -0
  7. package/dist/helpers/check-cumulative-markdown-length.d.ts +12 -0
  8. package/dist/helpers/check-cumulative-markdown-length.d.ts.map +1 -0
  9. package/dist/helpers/check-cumulative-markdown-length.js +23 -0
  10. package/dist/helpers/check-cumulative-markdown-length.js.map +1 -0
  11. package/dist/helpers/check-focus-on-load-uniqueness.d.ts +12 -0
  12. package/dist/helpers/check-focus-on-load-uniqueness.d.ts.map +1 -0
  13. package/dist/helpers/check-focus-on-load-uniqueness.js +52 -0
  14. package/dist/helpers/check-focus-on-load-uniqueness.js.map +1 -0
  15. package/dist/helpers/check-number-input-bounds.d.ts +12 -0
  16. package/dist/helpers/check-number-input-bounds.d.ts.map +1 -0
  17. package/dist/helpers/check-number-input-bounds.js +96 -0
  18. package/dist/helpers/check-number-input-bounds.js.map +1 -0
  19. package/dist/helpers/check-response-url-enabled-context.d.ts +25 -0
  20. package/dist/helpers/check-response-url-enabled-context.d.ts.map +1 -0
  21. package/dist/helpers/check-response-url-enabled-context.js +42 -0
  22. package/dist/helpers/check-response-url-enabled-context.js.map +1 -0
  23. package/dist/helpers/check-single-table-block.d.ts +10 -0
  24. package/dist/helpers/check-single-table-block.d.ts.map +1 -0
  25. package/dist/helpers/check-single-table-block.js +19 -0
  26. package/dist/helpers/check-single-table-block.js.map +1 -0
  27. package/dist/helpers/check-surface-compatibility.d.ts +22 -0
  28. package/dist/helpers/check-surface-compatibility.d.ts.map +1 -0
  29. package/dist/helpers/check-surface-compatibility.js +81 -0
  30. package/dist/helpers/check-surface-compatibility.js.map +1 -0
  31. package/dist/helpers/find-duplicate-block-ids.d.ts +17 -0
  32. package/dist/helpers/find-duplicate-block-ids.d.ts.map +1 -0
  33. package/dist/helpers/find-duplicate-block-ids.js +31 -0
  34. package/dist/helpers/find-duplicate-block-ids.js.map +1 -0
  35. package/dist/index.d.ts +11 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +11 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/slack-block-kit.schema.json +1605 -0
  40. package/dist/validate-block-kit.d.ts +33 -0
  41. package/dist/validate-block-kit.d.ts.map +1 -0
  42. package/dist/validate-block-kit.js +135 -0
  43. package/dist/validate-block-kit.js.map +1 -0
  44. package/package.json +64 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-surface-compatibility.d.ts","sourceRoot":"","sources":["../../src/helpers/check-surface-compatibility.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAyDnD;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAAE,EACjE,OAAO,EAAE,OAAO,GACf,MAAM,EAAE,CAsBV"}
@@ -0,0 +1,81 @@
1
+ // Source of truth: https://docs.slack.dev/blocks.json — the canonical JSON
2
+ // that powers the "Surfaces" column on each block's reference page. Every
3
+ // block entry there has an `available-in-surfaces` array valued with some
4
+ // subset of ["Modals", "Messages", "Home tabs"]. Whatever's missing from
5
+ // that array is what we forbid here.
6
+ // `file` is only produced by Slack when retrieving messages that contain
7
+ // remote files; apps cannot send it outbound on any surface
8
+ // (https://docs.slack.dev/reference/block-kit/blocks/file-block:
9
+ // "You can't add this block to app surfaces directly...").
10
+ const BLOCKS_NOT_ALLOWED_IN_MESSAGE = new Set(["alert", "file"]);
11
+ // carousel/card are listed as modal-available in Slack's blocks.json but
12
+ // empirically fail to render inside modal views — confirmed by the PR author
13
+ // against the live API. They work in messages and home tabs.
14
+ const BLOCKS_NOT_ALLOWED_IN_MODAL = new Set([
15
+ "card",
16
+ "carousel",
17
+ "context_actions",
18
+ "file",
19
+ "markdown",
20
+ "plan",
21
+ "table",
22
+ "task_card",
23
+ ]);
24
+ const BLOCKS_NOT_ALLOWED_IN_HOME = new Set([
25
+ "alert",
26
+ "context_actions",
27
+ "file",
28
+ "markdown",
29
+ "plan",
30
+ "table",
31
+ "task_card",
32
+ ]);
33
+ const FORBIDDEN_BY_SURFACE = {
34
+ message: BLOCKS_NOT_ALLOWED_IN_MESSAGE,
35
+ modal: BLOCKS_NOT_ALLOWED_IN_MODAL,
36
+ home: BLOCKS_NOT_ALLOWED_IN_HOME,
37
+ };
38
+ /**
39
+ * Per-surface block-count caps. Per
40
+ * https://docs.slack.dev/reference/block-kit/blocks:
41
+ * "You can include up to 50 blocks in each message, and 100 blocks in modals
42
+ * or Home tabs." The top-level schema enforces the looser 100 cap
43
+ * unconditionally so that modal/home payloads validate; the stricter message
44
+ * cap is applied here once the surface is known.
45
+ */
46
+ const MAX_BLOCKS_BY_SURFACE = {
47
+ message: 50,
48
+ modal: 100,
49
+ home: 100,
50
+ };
51
+ /**
52
+ * Reports any blocks whose type is not allowed on the given surface. Slack's
53
+ * surface compatibility matrix is documented at
54
+ * https://docs.slack.dev/surfaces/. Element-level restrictions (e.g. `file_input`
55
+ * only valid in modals) are checked here by looking at each input block's inner
56
+ * `element.type`.
57
+ * @param blocks - array of Block Kit blocks
58
+ * @param surface - target surface
59
+ * @returns array of error messages (empty when all blocks are surface-compatible)
60
+ */
61
+ export function checkSurfaceCompatibility(blocks, surface) {
62
+ const forbidden = FORBIDDEN_BY_SURFACE[surface];
63
+ const errors = [];
64
+ const maxBlocks = MAX_BLOCKS_BY_SURFACE[surface];
65
+ if (blocks.length > maxBlocks) {
66
+ errors.push(`surface '${surface}' allows at most ${maxBlocks} blocks (got ${blocks.length})`);
67
+ }
68
+ blocks.forEach((block, i) => {
69
+ if (block?.type && forbidden.has(block.type)) {
70
+ errors.push(`blocks[${i}].type '${block.type}' is not allowed on surface '${surface}'`);
71
+ // Block itself is already rejected — element-level errors would be
72
+ // redundant noise for the same misuse.
73
+ return;
74
+ }
75
+ if (block?.type === "input" && block.element?.type === "file_input" && surface !== "modal") {
76
+ errors.push(`blocks[${i}].element.type 'file_input' is only allowed in modal surfaces (got '${surface}')`);
77
+ }
78
+ });
79
+ return errors;
80
+ }
81
+ //# sourceMappingURL=check-surface-compatibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-surface-compatibility.js","sourceRoot":"","sources":["../../src/helpers/check-surface-compatibility.ts"],"names":[],"mappings":"AAMA,2EAA2E;AAC3E,0EAA0E;AAC1E,0EAA0E;AAC1E,yEAAyE;AACzE,qCAAqC;AACrC,yEAAyE;AACzE,4DAA4D;AAC5D,iEAAiE;AACjE,2DAA2D;AAC3D,MAAM,6BAA6B,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAEjE,yEAAyE;AACzE,6EAA6E;AAC7E,6DAA6D;AAC7D,MAAM,2BAA2B,GAAG,IAAI,GAAG,CAAC;IAC1C,MAAM;IACN,UAAU;IACV,iBAAiB;IACjB,MAAM;IACN,UAAU;IACV,MAAM;IACN,OAAO;IACP,WAAW;CACZ,CAAC,CAAC;AAEH,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC;IACzC,OAAO;IACP,iBAAiB;IACjB,MAAM;IACN,UAAU;IACV,MAAM;IACN,OAAO;IACP,WAAW;CACZ,CAAC,CAAC;AAEH,MAAM,oBAAoB,GAAiC;IACzD,OAAO,EAAE,6BAA6B;IACtC,KAAK,EAAE,2BAA2B;IAClC,IAAI,EAAE,0BAA0B;CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,qBAAqB,GAA4B;IACrD,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,GAAG;CACV,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAiE,EACjE,OAAgB;IAEhB,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,MAAM,SAAS,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,YAAY,OAAO,oBAAoB,SAAS,gBAAgB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,IAAI,KAAK,EAAE,IAAI,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,KAAK,CAAC,IAAI,gCAAgC,OAAO,GAAG,CAAC,CAAC;YACxF,mEAAmE;YACnE,uCAAuC;YACvC,OAAO;QACT,CAAC;QACD,IAAI,KAAK,EAAE,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,KAAK,YAAY,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YAC3F,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,uEAAuE,OAAO,IAAI,CAAC,CAAC;QAC7G,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Returns error messages describing any duplicate `block_id` values across the
3
+ * given blocks array. Pairs with the JSON Schema (draft 2020-12) in this
4
+ * package — JSON Schema can't express per-property uniqueness across items, so
5
+ * this check sits alongside structural validation.
6
+ *
7
+ * Skips `markdown` blocks: Slack drops their `block_id` on send ("The block_id
8
+ * is ignored in markdown blocks and will not be retained" per docs), so
9
+ * duplicates involving markdown blocks are false positives from Slack's POV.
10
+ * @param blocks - array of Block Kit blocks (or anything with an optional `block_id`)
11
+ * @returns array of human-readable error messages (empty when no duplicates)
12
+ */
13
+ export declare function findDuplicateBlockIds(blocks: readonly {
14
+ type?: string;
15
+ block_id?: string;
16
+ }[]): string[];
17
+ //# sourceMappingURL=find-duplicate-block-ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-duplicate-block-ids.d.ts","sourceRoot":"","sources":["../../src/helpers/find-duplicate-block-ids.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,MAAM,EAAE,CAgBvG"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Returns error messages describing any duplicate `block_id` values across the
3
+ * given blocks array. Pairs with the JSON Schema (draft 2020-12) in this
4
+ * package — JSON Schema can't express per-property uniqueness across items, so
5
+ * this check sits alongside structural validation.
6
+ *
7
+ * Skips `markdown` blocks: Slack drops their `block_id` on send ("The block_id
8
+ * is ignored in markdown blocks and will not be retained" per docs), so
9
+ * duplicates involving markdown blocks are false positives from Slack's POV.
10
+ * @param blocks - array of Block Kit blocks (or anything with an optional `block_id`)
11
+ * @returns array of human-readable error messages (empty when no duplicates)
12
+ */
13
+ export function findDuplicateBlockIds(blocks) {
14
+ const seen = new Map();
15
+ const errors = [];
16
+ blocks.forEach((block, i) => {
17
+ const id = block?.block_id;
18
+ if (id == null || block?.type === "markdown") {
19
+ return;
20
+ }
21
+ const prev = seen.get(id);
22
+ if (prev !== undefined) {
23
+ errors.push(`blocks[${i}].block_id must be unique — '${id}' appears at index ${prev} and ${i}`);
24
+ }
25
+ else {
26
+ seen.set(id, i);
27
+ }
28
+ });
29
+ return errors;
30
+ }
31
+ //# sourceMappingURL=find-duplicate-block-ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-duplicate-block-ids.js","sourceRoot":"","sources":["../../src/helpers/find-duplicate-block-ids.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAuD;IAC3F,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,EAAE,GAAG,KAAK,EAAE,QAAQ,CAAC;QAC3B,IAAI,EAAE,IAAI,IAAI,IAAI,KAAK,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;YAC7C,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,gCAAgC,EAAE,sBAAsB,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;QAClG,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export * from "./helpers/check-card-actions-max";
2
+ export * from "./helpers/check-cumulative-markdown-length";
3
+ export * from "./helpers/check-focus-on-load-uniqueness";
4
+ export * from "./helpers/check-number-input-bounds";
5
+ export * from "./helpers/check-response-url-enabled-context";
6
+ export * from "./helpers/check-single-table-block";
7
+ export * from "./helpers/check-surface-compatibility";
8
+ export * from "./helpers/find-duplicate-block-ids";
9
+ export { default as slackBlockKitSchema } from "./slack-block-kit.schema.json";
10
+ export * from "./validate-block-kit";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAC;AACjD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,oCAAoC,CAAC;AACnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAC/E,cAAc,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export * from "./helpers/check-card-actions-max";
2
+ export * from "./helpers/check-cumulative-markdown-length";
3
+ export * from "./helpers/check-focus-on-load-uniqueness";
4
+ export * from "./helpers/check-number-input-bounds";
5
+ export * from "./helpers/check-response-url-enabled-context";
6
+ export * from "./helpers/check-single-table-block";
7
+ export * from "./helpers/check-surface-compatibility";
8
+ export * from "./helpers/find-duplicate-block-ids";
9
+ export { default as slackBlockKitSchema } from "./slack-block-kit.schema.json";
10
+ export * from "./validate-block-kit";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAC;AACjD,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,oCAAoC,CAAC;AACnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAC/E,cAAc,sBAAsB,CAAC"}