@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,33 @@
1
+ import { type Surface } from "./helpers/check-surface-compatibility";
2
+ /**
3
+ * Result of a Block Kit validation.
4
+ */
5
+ export interface ValidationResult {
6
+ valid: boolean;
7
+ errors: string[];
8
+ }
9
+ /**
10
+ * Payload targets recognized by the wrapper. `blocks` validates a bare blocks
11
+ * array; `modal` / `home` validate a view envelope.
12
+ */
13
+ export type ValidationTarget = "blocks" | "modal" | "home";
14
+ export interface ValidateBlockKitOptions {
15
+ /** Which top-level shape to validate. Defaults to `blocks`. */
16
+ target?: ValidationTarget;
17
+ /**
18
+ * Which Slack surface the payload will render on. Used to enforce surface
19
+ * compatibility. When `target` is `modal` or `home`, the surface is derived
20
+ * automatically.
21
+ */
22
+ surface?: Surface;
23
+ }
24
+ /**
25
+ * Validates a Slack Block Kit payload against the JSON Schema and the set of
26
+ * caveat helpers (duplicate block_ids, cumulative markdown length, single-table,
27
+ * focus_on_load uniqueness, surface compatibility).
28
+ * @param input - the payload to validate
29
+ * @param options - target shape + optional surface
30
+ * @returns `{ valid, errors }` — `errors` is a flat array of human-readable messages
31
+ */
32
+ export declare function validateBlockKit(input: unknown, options?: ValidateBlockKitOptions): ValidationResult;
33
+ //# sourceMappingURL=validate-block-kit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-block-kit.d.ts","sourceRoot":"","sources":["../src/validate-block-kit.ts"],"names":[],"mappings":"AAQA,OAAO,EAA6B,KAAK,OAAO,EAAE,MAAM,uCAAuC,CAAC;AAIhG;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACtC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAqGD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,uBAA4B,GAAG,gBAAgB,CAkCxG"}
@@ -0,0 +1,135 @@
1
+ import Ajv2020 from "ajv/dist/2020";
2
+ import addFormats from "ajv-formats";
3
+ import { checkCardActionsMax } from "./helpers/check-card-actions-max";
4
+ import { checkCumulativeMarkdownLength } from "./helpers/check-cumulative-markdown-length";
5
+ import { checkFocusOnLoadUniqueness } from "./helpers/check-focus-on-load-uniqueness";
6
+ import { checkNumberInputBounds } from "./helpers/check-number-input-bounds";
7
+ import { checkResponseUrlEnabledContext } from "./helpers/check-response-url-enabled-context";
8
+ import { checkSingleTableBlock } from "./helpers/check-single-table-block";
9
+ import { checkSurfaceCompatibility } from "./helpers/check-surface-compatibility";
10
+ import { findDuplicateBlockIds } from "./helpers/find-duplicate-block-ids";
11
+ import schema from "./slack-block-kit.schema.json";
12
+ const ajv = new Ajv2020({ allErrors: true, strict: false });
13
+ addFormats(ajv);
14
+ ajv.addSchema(schema, "slack-block-kit");
15
+ const compile = (ref) => ajv.compile({ $ref: `slack-block-kit#/$defs/${ref}` });
16
+ const validators = {
17
+ blocks: ajv.compile({ $ref: "slack-block-kit" }),
18
+ modal: compile("modal_view"),
19
+ home: compile("home_view"),
20
+ };
21
+ const formatAjvError = (err) => {
22
+ const path = err.instancePath || "(root)";
23
+ return `${path} ${err.message ?? "failed validation"}`;
24
+ };
25
+ const resolveSurface = (target, surface) => {
26
+ if (target === "modal") {
27
+ return "modal";
28
+ }
29
+ if (target === "home") {
30
+ return "home";
31
+ }
32
+ return surface;
33
+ };
34
+ const extractBlocks = (input, target) => {
35
+ if (target === "blocks") {
36
+ return Array.isArray(input) ? input : [];
37
+ }
38
+ if (input != null && typeof input === "object" && "blocks" in input) {
39
+ const { blocks } = input;
40
+ return Array.isArray(blocks) ? blocks : [];
41
+ }
42
+ return [];
43
+ };
44
+ /**
45
+ * Cap for recursive traversal of payloads. Real Slack payloads are bounded by
46
+ * their structural limits (≤100 blocks, ≤25 elements per block, etc.), so
47
+ * genuine nesting never comes close to 50. Stop there to guard against
48
+ * adversarial or malformed inputs. Matches the cap used in the other walkers
49
+ * (`check-focus-on-load-uniqueness`, `check-number-input-bounds`).
50
+ */
51
+ const MAX_STRIP_DEPTH = 50;
52
+ /**
53
+ * Recursively normalizes a payload to mirror the shape Slack will actually see
54
+ * after `JSON.stringify`:
55
+ * - drops object properties whose value is `undefined` (so common builder
56
+ * patterns like `value: foo ?? undefined` don't trip `additionalProperties:
57
+ * false`)
58
+ * - replaces `undefined` array slots with `null` (matching
59
+ * `JSON.stringify([undefined]) === '[null]'`), so schemas that accept
60
+ * explicit null placeholders still pass
61
+ *
62
+ * Depth-limited at {@link MAX_STRIP_DEPTH} to avoid stack overflow on
63
+ * pathological inputs — nodes beyond that depth are returned as-is.
64
+ * @param input - any value that may contain undefined-valued properties
65
+ * @param depth - internal recursion depth counter
66
+ * @returns a deep copy of the input with undefined properties removed and
67
+ * undefined array slots replaced with null
68
+ */
69
+ const stripUndefined = (input, depth = 0) => {
70
+ if (depth > MAX_STRIP_DEPTH) {
71
+ return input;
72
+ }
73
+ if (Array.isArray(input)) {
74
+ // Use a manual length-indexed loop (not `.map`) so that sparse holes are
75
+ // visited and normalized to null — `Array.prototype.map` skips missing
76
+ // indices, but `JSON.stringify([, 1])` emits `'[null,1]'`, so the wire
77
+ // shape includes nulls.
78
+ const result = new Array(input.length);
79
+ for (let i = 0; i < input.length; i++) {
80
+ const item = input[i];
81
+ result[i] = item === undefined ? null : stripUndefined(item, depth + 1);
82
+ }
83
+ return result;
84
+ }
85
+ if (input !== null && typeof input === "object") {
86
+ const result = {};
87
+ for (const [key, value] of Object.entries(input)) {
88
+ // Skip prototype-pollution vectors. A payload containing `__proto__`,
89
+ // `constructor`, or `prototype` as a key would otherwise be assigned
90
+ // via bracket notation into `result`, which can mutate the prototype
91
+ // chain instead of setting a regular property.
92
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
93
+ continue;
94
+ }
95
+ if (value !== undefined) {
96
+ result[key] = stripUndefined(value, depth + 1);
97
+ }
98
+ }
99
+ return result;
100
+ }
101
+ return input;
102
+ };
103
+ /**
104
+ * Validates a Slack Block Kit payload against the JSON Schema and the set of
105
+ * caveat helpers (duplicate block_ids, cumulative markdown length, single-table,
106
+ * focus_on_load uniqueness, surface compatibility).
107
+ * @param input - the payload to validate
108
+ * @param options - target shape + optional surface
109
+ * @returns `{ valid, errors }` — `errors` is a flat array of human-readable messages
110
+ */
111
+ export function validateBlockKit(input, options = {}) {
112
+ const target = options.target ?? "blocks";
113
+ const surface = resolveSurface(target, options.surface);
114
+ const normalizedInput = stripUndefined(input);
115
+ const errors = [];
116
+ const validator = validators[target];
117
+ if (!validator(normalizedInput)) {
118
+ for (const err of validator.errors ?? []) {
119
+ errors.push(formatAjvError(err));
120
+ }
121
+ }
122
+ const blocks = extractBlocks(normalizedInput, target);
123
+ errors.push(...findDuplicateBlockIds(blocks));
124
+ errors.push(...checkCumulativeMarkdownLength(blocks));
125
+ errors.push(...checkSingleTableBlock(blocks));
126
+ errors.push(...checkCardActionsMax(blocks));
127
+ errors.push(...checkFocusOnLoadUniqueness(blocks));
128
+ errors.push(...checkNumberInputBounds(blocks));
129
+ errors.push(...checkResponseUrlEnabledContext(blocks, surface));
130
+ if (surface) {
131
+ errors.push(...checkSurfaceCompatibility(blocks, surface));
132
+ }
133
+ return { valid: errors.length === 0, errors };
134
+ }
135
+ //# sourceMappingURL=validate-block-kit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-block-kit.js","sourceRoot":"","sources":["../src/validate-block-kit.ts"],"names":[],"mappings":"AAAA,OAAO,OAAoD,MAAM,eAAe,CAAC;AACjF,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,6BAA6B,EAAE,MAAM,4CAA4C,CAAC;AAC3F,OAAO,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAgB,MAAM,uCAAuC,CAAC;AAChG,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,MAAM,MAAM,+BAA+B,CAAC;AA2BnD,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5D,UAAU,CAAC,GAAG,CAAC,CAAC;AAChB,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAEzC,MAAM,OAAO,GAAG,CAAC,GAAW,EAAoB,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,0BAA0B,GAAG,EAAE,EAAE,CAAC,CAAC;AAE1G,MAAM,UAAU,GAAG;IACjB,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAChD,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC;IAC5B,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC;CAClB,CAAC;AAEX,MAAM,cAAc,GAAG,CAAC,GAAgB,EAAU,EAAE;IAClD,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,IAAI,QAAQ,CAAC;IAC1C,OAAO,GAAG,IAAI,IAAI,GAAG,CAAC,OAAO,IAAI,mBAAmB,EAAE,CAAC;AACzD,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,MAAwB,EAAE,OAA4B,EAAuB,EAAE;IACrG,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,KAAc,EAAE,MAAwB,EAAsB,EAAE;IACrF,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3C,CAAC;IACD,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;QACpE,MAAM,EAAE,MAAM,EAAE,GAAG,KAA4B,CAAC;QAChD,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,cAAc,GAAG,CAAC,KAAc,EAAE,KAAK,GAAG,CAAC,EAAW,EAAE;IAC5D,IAAI,KAAK,GAAG,eAAe,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,yEAAyE;QACzE,uEAAuE;QACvE,uEAAuE;QACvE,wBAAwB;QACxB,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YAC5E,sEAAsE;YACtE,qEAAqE;YACrE,qEAAqE;YACrE,+CAA+C;YAC/C,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;gBACxE,SAAS;YACX,CAAC;YACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc,EAAE,UAAmC,EAAE;IACpF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC;IAC1C,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAExD,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC;QAChC,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,eAAe,EAAE,MAAM,CAKjD,CAAC;IAEJ,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,IAAI,CAAC,GAAG,6BAA6B,CAAC,MAAM,CAAC,CAAC,CAAC;IACtD,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,MAAM,CAAC,IAAI,CAAC,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC,CAAC;IACnD,MAAM,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/C,MAAM,CAAC,IAAI,CAAC,GAAG,8BAA8B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,CAAC,IAAI,CAAC,GAAG,yBAAyB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AAChD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@tightknitai/slack-block-kit-validator",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "JSON Schema (draft 2020-12) and validation helpers for Slack Block Kit payloads.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./schema.json": "./dist/slack-block-kit.schema.json"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "sideEffects": false,
19
+ "dependencies": {
20
+ "ajv": "^8.18.0",
21
+ "ajv-formats": "^3.0.1"
22
+ },
23
+ "devDependencies": {
24
+ "@biomejs/biome": "^2.4.11",
25
+ "@vitest/coverage-v8": "^4.1.4",
26
+ "ajv-cli": "^5.0.0",
27
+ "lefthook": "^2.1.5",
28
+ "typescript": "^5.9.0",
29
+ "vitest": "^4.1.4"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/TightknitAI/slack-block-kit-validator.git"
34
+ },
35
+ "homepage": "https://github.com/TightknitAI/slack-block-kit-validator",
36
+ "bugs": {
37
+ "url": "https://github.com/TightknitAI/slack-block-kit-validator/issues"
38
+ },
39
+ "author": "Tightknit <https://tightknit.ai>",
40
+ "license": "MIT",
41
+ "engines": {
42
+ "node": ">=20"
43
+ },
44
+ "keywords": [
45
+ "slack",
46
+ "block-kit",
47
+ "json-schema",
48
+ "validator",
49
+ "ajv"
50
+ ],
51
+ "scripts": {
52
+ "build": "tsc && cp src/slack-block-kit.schema.json dist/slack-block-kit.schema.json",
53
+ "build:clean": "rm -rf dist && pnpm run build",
54
+ "typecheck": "tsc --noEmit",
55
+ "test": "vitest run",
56
+ "test:watch": "vitest",
57
+ "test:coverage": "vitest run --coverage",
58
+ "lint": "biome check .",
59
+ "lint:fix": "biome check --fix .",
60
+ "format": "biome format --write .",
61
+ "validate-schema": "ajv compile --spec=draft2020 --strict=false -c ajv-formats -s src/slack-block-kit.schema.json",
62
+ "compile:standalone": "mkdir -p dist && ajv compile --spec=draft2020 --strict=false -c ajv-formats -s src/slack-block-kit.schema.json -o dist/standalone-validator.js"
63
+ }
64
+ }