@tappify/extension-sdk 2.0.0 → 2.1.0-beta.1

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 (148) hide show
  1. package/README.md +208 -0
  2. package/dist/chunk-2CY5HVY7.mjs +59 -0
  3. package/dist/chunk-2CY5HVY7.mjs.map +1 -0
  4. package/dist/{chunk-2DBSVKAU.cjs → chunk-6HETNZJY.cjs} +1 -1
  5. package/dist/chunk-6HETNZJY.cjs.map +1 -0
  6. package/dist/{chunk-PYJMJF44.cjs → chunk-7YKH4LZN.cjs} +9 -2
  7. package/dist/chunk-7YKH4LZN.cjs.map +1 -0
  8. package/dist/{chunk-RMUWRESK.cjs → chunk-A74HCO6T.cjs} +31 -18
  9. package/dist/chunk-A74HCO6T.cjs.map +1 -0
  10. package/dist/chunk-BPCGXXMD.cjs +781 -0
  11. package/dist/chunk-BPCGXXMD.cjs.map +1 -0
  12. package/dist/chunk-EB5XWQAU.cjs +132 -0
  13. package/dist/chunk-EB5XWQAU.cjs.map +1 -0
  14. package/dist/{chunk-5AAPD5WV.cjs → chunk-GAUZYBXE.cjs} +11 -11
  15. package/dist/chunk-GAUZYBXE.cjs.map +1 -0
  16. package/dist/chunk-J5LGTIGS.mjs +10 -0
  17. package/dist/chunk-J5LGTIGS.mjs.map +1 -0
  18. package/dist/{chunk-ZT35X7KL.mjs → chunk-KZ5NGFUP.mjs} +22 -9
  19. package/dist/chunk-KZ5NGFUP.mjs.map +1 -0
  20. package/dist/chunk-NN263PGQ.cjs +70 -0
  21. package/dist/chunk-NN263PGQ.cjs.map +1 -0
  22. package/dist/{chunk-3YWVYA42.cjs → chunk-O357OJXL.cjs} +14 -18
  23. package/dist/chunk-O357OJXL.cjs.map +1 -0
  24. package/dist/chunk-ONJMPO3P.cjs +59 -0
  25. package/dist/chunk-ONJMPO3P.cjs.map +1 -0
  26. package/dist/{chunk-OPPYEA7F.cjs → chunk-PRCZMOLD.cjs} +3 -3
  27. package/dist/chunk-PRCZMOLD.cjs.map +1 -0
  28. package/dist/chunk-Q7SFCCGT.cjs +10 -0
  29. package/dist/chunk-Q7SFCCGT.cjs.map +1 -0
  30. package/dist/{chunk-QHM7XNRK.mjs → chunk-QA5AL3VZ.mjs} +2 -2
  31. package/dist/{chunk-QHM7XNRK.mjs.map → chunk-QA5AL3VZ.mjs.map} +1 -1
  32. package/dist/chunk-QJMZRV7A.mjs +132 -0
  33. package/dist/chunk-QJMZRV7A.mjs.map +1 -0
  34. package/dist/{chunk-CSTYBKZC.mjs → chunk-QXD7WVKK.mjs} +1 -1
  35. package/dist/chunk-QXD7WVKK.mjs.map +1 -0
  36. package/dist/{chunk-4C5Z3CIK.mjs → chunk-RBIQT75L.mjs} +2 -2
  37. package/dist/chunk-RBIQT75L.mjs.map +1 -0
  38. package/dist/{chunk-MNDVYPEP.mjs → chunk-SGCYEEXD.mjs} +9 -2
  39. package/dist/chunk-SGCYEEXD.mjs.map +1 -0
  40. package/dist/{chunk-BDCX6LRQ.mjs → chunk-SGEYDV3K.mjs} +3 -3
  41. package/dist/chunk-SGEYDV3K.mjs.map +1 -0
  42. package/dist/{chunk-IGK2AP6J.mjs → chunk-SLGOHIBL.mjs} +13 -17
  43. package/dist/chunk-SLGOHIBL.mjs.map +1 -0
  44. package/dist/chunk-SWG7CNTZ.mjs +781 -0
  45. package/dist/chunk-SWG7CNTZ.mjs.map +1 -0
  46. package/dist/{chunk-7KIKOH5H.cjs → chunk-VV7ELNRW.cjs} +3 -3
  47. package/dist/chunk-VV7ELNRW.cjs.map +1 -0
  48. package/dist/chunk-Z43DPNZ6.mjs +70 -0
  49. package/dist/chunk-Z43DPNZ6.mjs.map +1 -0
  50. package/dist/client-CV1qzQgx.d.cts +25 -0
  51. package/dist/client-CnlSHDWE.d.ts +25 -0
  52. package/dist/codegen.cjs.map +1 -1
  53. package/dist/codegen.d.cts +40 -7
  54. package/dist/codegen.d.ts +40 -7
  55. package/dist/codegen.mjs.map +1 -1
  56. package/dist/{constants-DJ_KgZpN.d.cts → constants-BAimnqOK.d.cts} +41 -8
  57. package/dist/{constants-DJ_KgZpN.d.ts → constants-BAimnqOK.d.ts} +41 -8
  58. package/dist/{context-Ml7k_V14.d.ts → context-Cr93B05o.d.ts} +5 -4
  59. package/dist/{context-Dt6xh_5t.d.cts → context-DJ3REtdT.d.cts} +5 -4
  60. package/dist/{errors-COsBh7kd.d.cts → errors-Cqh0wrqt.d.cts} +3 -2
  61. package/dist/{errors-COsBh7kd.d.ts → errors-Cqh0wrqt.d.ts} +3 -2
  62. package/dist/host.cjs +5 -4
  63. package/dist/host.cjs.map +1 -1
  64. package/dist/host.d.cts +5 -5
  65. package/dist/host.d.ts +5 -5
  66. package/dist/host.mjs +4 -3
  67. package/dist/host.mjs.map +1 -1
  68. package/dist/index.cjs +9 -656
  69. package/dist/index.cjs.map +1 -1
  70. package/dist/index.d.cts +69 -627
  71. package/dist/index.d.ts +69 -627
  72. package/dist/index.mjs +5 -652
  73. package/dist/index.mjs.map +1 -1
  74. package/dist/manifest.cjs +138 -85
  75. package/dist/manifest.cjs.map +1 -1
  76. package/dist/manifest.d.cts +93 -12
  77. package/dist/manifest.d.ts +93 -12
  78. package/dist/manifest.mjs +125 -72
  79. package/dist/manifest.mjs.map +1 -1
  80. package/dist/server.cjs +15 -66
  81. package/dist/server.cjs.map +1 -1
  82. package/dist/server.d.cts +6 -6
  83. package/dist/server.d.ts +6 -6
  84. package/dist/server.mjs +10 -61
  85. package/dist/server.mjs.map +1 -1
  86. package/dist/testing/index.cjs +73 -750
  87. package/dist/testing/index.cjs.map +1 -1
  88. package/dist/testing/index.d.cts +30 -11
  89. package/dist/testing/index.d.ts +30 -11
  90. package/dist/testing/index.mjs +89 -766
  91. package/dist/testing/index.mjs.map +1 -1
  92. package/dist/testing/jest.cjs +6 -5
  93. package/dist/testing/jest.cjs.map +1 -1
  94. package/dist/testing/jest.mjs +3 -2
  95. package/dist/testing/jest.mjs.map +1 -1
  96. package/dist/testing/vitest.cjs +6 -5
  97. package/dist/testing/vitest.cjs.map +1 -1
  98. package/dist/testing/vitest.d.cts +10 -0
  99. package/dist/testing/vitest.d.ts +10 -0
  100. package/dist/testing/vitest.mjs +3 -2
  101. package/dist/testing/vitest.mjs.map +1 -1
  102. package/dist/{types-DrpQSv_2.d.cts → types-BYGcp5pZ.d.ts} +4 -3
  103. package/dist/{types-DDAGLpov.d.ts → types-I1JOZyI_.d.cts} +4 -3
  104. package/dist/ui/styles.css +740 -0
  105. package/dist/ui.cjs +1018 -0
  106. package/dist/ui.cjs.map +1 -0
  107. package/dist/ui.d.cts +231 -0
  108. package/dist/ui.d.ts +231 -0
  109. package/dist/ui.mjs +1018 -0
  110. package/dist/ui.mjs.map +1 -0
  111. package/dist/vite-preview-bootstrap.cjs +66 -0
  112. package/dist/vite-preview-bootstrap.cjs.map +1 -0
  113. package/dist/vite-preview-bootstrap.d.cts +15 -0
  114. package/dist/vite-preview-bootstrap.d.ts +15 -0
  115. package/dist/vite-preview-bootstrap.mjs +66 -0
  116. package/dist/vite-preview-bootstrap.mjs.map +1 -0
  117. package/dist/vite-preview.cjs +876 -0
  118. package/dist/vite-preview.cjs.map +1 -0
  119. package/dist/vite-preview.d.cts +9 -0
  120. package/dist/vite-preview.d.ts +9 -0
  121. package/dist/vite-preview.mjs +876 -0
  122. package/dist/vite-preview.mjs.map +1 -0
  123. package/dist/vite.cjs +316 -59
  124. package/dist/vite.cjs.map +1 -1
  125. package/dist/vite.d.cts +51 -8
  126. package/dist/vite.d.ts +51 -8
  127. package/dist/vite.mjs +308 -53
  128. package/dist/vite.mjs.map +1 -1
  129. package/dist/work-events-DB3WR468.d.ts +547 -0
  130. package/dist/work-events-Dxe7Djdy.d.cts +547 -0
  131. package/package.json +19 -2
  132. package/schema/extension-v2.json +432 -121
  133. package/dist/chunk-2DBSVKAU.cjs.map +0 -1
  134. package/dist/chunk-3YWVYA42.cjs.map +0 -1
  135. package/dist/chunk-4C5Z3CIK.mjs.map +0 -1
  136. package/dist/chunk-5AAPD5WV.cjs.map +0 -1
  137. package/dist/chunk-7KIKOH5H.cjs.map +0 -1
  138. package/dist/chunk-BDCX6LRQ.mjs.map +0 -1
  139. package/dist/chunk-CSTYBKZC.mjs.map +0 -1
  140. package/dist/chunk-IGK2AP6J.mjs.map +0 -1
  141. package/dist/chunk-MNDVYPEP.mjs.map +0 -1
  142. package/dist/chunk-OPPYEA7F.cjs.map +0 -1
  143. package/dist/chunk-PYJMJF44.cjs.map +0 -1
  144. package/dist/chunk-RMUWRESK.cjs.map +0 -1
  145. package/dist/chunk-ZT35X7KL.mjs.map +0 -1
  146. package/dist/handler-CZIlZ5vu.d.ts +0 -227
  147. package/dist/handler-kU9Jf36K.d.cts +0 -227
  148. package/dist/styles.css +0 -367
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/manifest/define.ts","../src/manifest/schema.ts"],"sourcesContent":["import type {\n ExposableKind,\n ExtensionManifest,\n UiContributionRef,\n} from './types';\n\nconst EXPOSE_FOLDER: Record<Exclude<ExposableKind, 'settings'>, string> = {\n page: 'pages',\n tab: 'tabs',\n widget: 'widgets',\n rowAction: 'rowActions',\n};\n\nexport function defineManifest(manifest: ExtensionManifest): ExtensionManifest {\n return manifest;\n}\n\nexport function exposeName(kind: 'settings'): string;\nexport function exposeName(\n kind: Exclude<ExposableKind, 'settings'>,\n id: string,\n): string;\nexport function exposeName(kind: ExposableKind, id?: string): string {\n if (kind === 'settings') return './settings';\n return `./${EXPOSE_FOLDER[kind]}/${id ?? ''}`;\n}\n\nexport function remoteName(extensionId: string): string {\n return `ext_${extensionId.replace(/-/g, '_')}`;\n}\n\nexport function uiContributions(\n manifest: ExtensionManifest,\n): UiContributionRef[] {\n const contributes = manifest.contributes;\n const refs: UiContributionRef[] = [];\n\n for (const page of contributes.pages ?? []) {\n refs.push({\n kind: 'page',\n id: page.id,\n entry: page.entry,\n expose: exposeName('page', page.id),\n });\n }\n for (const tab of contributes.tabs ?? []) {\n refs.push({\n kind: 'tab',\n id: tab.id,\n entry: tab.entry,\n expose: exposeName('tab', tab.id),\n });\n }\n for (const widget of contributes.widgets ?? []) {\n refs.push({\n kind: 'widget',\n id: widget.id,\n entry: widget.entry,\n expose: exposeName('widget', widget.id),\n });\n }\n for (const rowAction of contributes.rowActions ?? []) {\n refs.push({\n kind: 'rowAction',\n id: rowAction.id,\n entry: rowAction.entry,\n expose: exposeName('rowAction', rowAction.id),\n });\n }\n if (contributes.settings) {\n refs.push({\n kind: 'settings',\n id: 'settings',\n entry: contributes.settings.entry,\n expose: exposeName('settings'),\n });\n }\n\n return refs;\n}\n","import { z } from 'zod';\nimport {\n CATEGORIES,\n CHARTS,\n EVENTS,\n PAGE_KEYS,\n SCOPE_KEYS,\n SERVED_SDK_MAJORS,\n SLOTS,\n TABLE_KEYS,\n findScope,\n slotsForPage,\n} from './constants';\nimport type {\n ExtensionManifest,\n ManifestIssue,\n ManifestValidation,\n} from './types';\n\nconst SLUG = /^[a-z][a-z0-9-]{2,39}$/;\nconst CONTRIBUTION_ID = /^[a-z][a-z0-9-]{1,39}$/;\nconst SNAKE_CASE = /^[a-z][a-z0-9_]*$/;\nconst PROCEDURE_NAME = /^[a-zA-Z][a-zA-Z0-9_-]{1,59}$/;\nconst DOMAIN_EVENT = /^[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*$/;\n\nconst jsonSchema = z\n .record(z.string(), z.unknown())\n .describe(\n 'A JSON Schema object. `tappify extension types` turns it into a TypeScript type.',\n );\n\n/** The serialised size Tappify accepts for a tool or action input schema. */\nexport const AI_INPUT_SCHEMA_MAX_BYTES = 16_384;\n\n/** The length Tappify accepts for one `title` or `description` inside it. */\nexport const AI_SCHEMA_TEXT_MAX = 500;\n\nconst AI_SCHEMA_TEXT_KEYS = new Set(['title', 'description']);\nconst AI_SCHEMA_MAX_DEPTH = 12;\n\nfunction overlongSchemaText(value: unknown, depth = 0): string | null {\n if (depth > AI_SCHEMA_MAX_DEPTH) return null;\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n const found = overlongSchemaText(entry, depth + 1);\n if (found !== null) return found;\n }\n return null;\n }\n\n if (typeof value !== 'object' || value === null) return null;\n\n for (const [key, entry] of Object.entries(value)) {\n if (\n AI_SCHEMA_TEXT_KEYS.has(key) &&\n typeof entry === 'string' &&\n entry.length > AI_SCHEMA_TEXT_MAX\n ) {\n return key;\n }\n const found = overlongSchemaText(entry, depth + 1);\n if (found !== null) return found;\n }\n\n return null;\n}\n\n/**\n * The assistant reads the `title` and `description` strings inside a tool or\n * action input schema the same way it reads the tool description, so Tappify\n * bounds them the same way. A schema past either bound is refused at publish,\n * and the CLI refuses it here rather than letting you find out on upload.\n */\nconst aiInputSchema = jsonSchema.superRefine((value, ctx) => {\n const size = new TextEncoder().encode(JSON.stringify(value)).length;\n if (size > AI_INPUT_SCHEMA_MAX_BYTES) {\n ctx.addIssue({\n code: 'custom',\n message: `This input schema serialises to ${size} bytes; Tappify accepts at most ${AI_INPUT_SCHEMA_MAX_BYTES}. Describe the arguments in fewer words, or take fewer of them.`,\n });\n }\n\n const overlong = overlongSchemaText(value);\n if (overlong !== null) {\n ctx.addIssue({\n code: 'custom',\n message: `A \"${overlong}\" inside this input schema is longer than ${AI_SCHEMA_TEXT_MAX} characters. The assistant reads it like the tool description, which is bounded the same way.`,\n });\n }\n});\n\nconst httpsUrl = z.url().startsWith('https://');\nconst entryPath = z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The source file that renders this contribution, as a path relative to the manifest. It has to be present as an expose in the built bundle.',\n );\nconst contributionId = z\n .string()\n .regex(CONTRIBUTION_ID)\n .describe(\n 'The id for this contribution, and the handle the host and your generated types refer to it by.',\n );\nconst title = z\n .string()\n .min(1)\n .max(60)\n .describe('The label owners read on this contribution.');\n\nconst scopeEntry = z\n .object({\n key: z\n .enum(SCOPE_KEYS, {\n error: 'Unknown scope key; see the Scopes reference.',\n })\n .describe('The scope key, from the scopes reference.'),\n justification: z\n .string()\n .min(20)\n .max(500)\n .describe(\n 'Why this extension asks for the scope, in your own words. Shown on the consent screen, and required for the scopes that ask for one.',\n )\n .optional(),\n })\n .strict();\n\nconst mobile = z\n .boolean()\n .describe(\n 'Whether this surface is drawn on phone layouts. Defaults to true; set false to hide it there.',\n )\n .optional();\n\nconst hostPage = (what: string) => z.enum(PAGE_KEYS).describe(what);\n\nconst pageContribution = z\n .object({\n id: contributionId,\n title,\n entry: entryPath,\n nav: z\n .boolean()\n .describe(\n 'Whether this page also gets an entry in the project navigation. The route `/projects/:projectId/ext/:extensionId/:pageId` exists either way.',\n )\n .optional(),\n mobile,\n })\n .strict();\n\nconst tabContribution = z\n .object({\n id: contributionId,\n title,\n entry: entryPath,\n page: hostPage('The host page this tab sits on.'),\n mobile,\n })\n .strict();\n\nconst widgetContribution = z\n .object({\n id: contributionId,\n title,\n entry: entryPath,\n page: hostPage('The host page this widget renders on.'),\n slot: z\n .enum(SLOTS)\n .describe(\n 'The named anchor on that page. A slot that is not on the page fails validation.',\n ),\n size: z\n .enum(['1x1', '2x1', '2x2'])\n .describe(\n \"The widget's footprint in the slot grid, as columns by rows. Every slot takes all three.\",\n ),\n expandable: z\n .boolean()\n .describe(\n 'Whether owners can open the same component in the expand panel.',\n )\n .optional(),\n })\n .strict();\n\nconst seriesContribution = z\n .object({\n id: contributionId,\n label: title,\n chart: z.enum(CHARTS).describe('The host chart this line is drawn on.'),\n metric: z\n .string()\n .min(1)\n .max(60)\n .describe(\n 'The connector metric this line plots. It has to be one the connector declares.',\n ),\n })\n .strict();\n\nconst bannerContribution = z\n .object({\n id: contributionId,\n page: hostPage('The host page this banner appears on.'),\n event: z\n .string()\n .regex(DOMAIN_EVENT)\n .describe(\n 'The event that raises it. Declare the same name under `contributes.webhooks` with `as: \"banner\"`.',\n ),\n })\n .strict();\n\nconst rowActionContribution = z\n .object({\n id: contributionId,\n title,\n table: z\n .enum(TABLE_KEYS)\n .describe('The host table whose row menu gains this entry.'),\n entry: entryPath,\n })\n .strict();\n\nconst markerContribution = z\n .object({\n id: contributionId,\n label: title,\n chart: z\n .union([z.enum(CHARTS), z.literal('*')])\n .describe(\n 'The host chart this marker is drawn on, or `*` for every chart.',\n ),\n event: z\n .string()\n .regex(DOMAIN_EVENT)\n .describe(\n 'The event that places it on the time axis. Declare the same name under `contributes.webhooks` with `as: \"event\"`.',\n ),\n glyph: z\n .string()\n .min(1)\n .max(2)\n .describe('The one or two characters drawn at the marker.'),\n })\n .strict();\n\nconst settingsContribution = z\n .object({\n entry: entryPath,\n schema: jsonSchema.describe(\n 'The JSON Schema for the values owners fill in. It becomes `TapSettingsProps[\"values\"]` and the reserved `settings` singleton.',\n ),\n })\n .strict();\n\nconst storageCollection = z\n .object({\n scope: z\n .enum(['user', 'install', 'organization'])\n .describe(\n 'Who the documents belong to: one `user`, the whole `install`, or the `organization`.',\n ),\n singleton: z\n .boolean()\n .describe(\n 'Whether the collection holds exactly one document rather than many.',\n )\n .optional(),\n schema: jsonSchema.describe(\n 'The JSON Schema for one document in this collection.',\n ),\n })\n .strict();\n\nconst credentialField = z\n .object({\n name: z\n .string()\n .regex(SNAKE_CASE)\n .describe('The key your server reads this value back as.'),\n label: z\n .string()\n .min(1)\n .max(60)\n .describe('The label owners read above the input.'),\n type: z\n .enum(['text', 'password', 'textarea', 'file', 'select'])\n .describe('How the input is drawn to the owner.'),\n required: z\n .boolean()\n .describe('Whether owners have to fill it in before the install works.'),\n options: z\n .array(z.string())\n .describe('The choices offered, for a `select` field.')\n .optional(),\n help: z\n .string()\n .max(200)\n .describe('The hint drawn under the input.')\n .optional(),\n })\n .strict();\n\nconst connectorContribution = z\n .object({\n auth: z\n .object({\n method: z\n .enum(['none', 'api_key', 'oauth'])\n .describe('The credential flow owners go through at install.'),\n fields: z\n .array(credentialField)\n .max(20)\n .describe(\n 'The credentials owners enter. They reach your server as `credentials` on every call.',\n )\n .optional(),\n oauth: z\n .object({\n authorizationUrl: httpsUrl.describe(\n 'Where owners are sent to approve access.',\n ),\n tokenUrl: httpsUrl.describe(\n 'Where the authorization code is exchanged for a token.',\n ),\n scopes: z\n .array(z.string())\n .describe(\n \"The scopes asked of the outside service. These are that service's scopes, not Tappify's.\",\n ),\n })\n .strict()\n .describe('The endpoints, for `method: \"oauth\"`.')\n .optional(),\n })\n .strict()\n .describe('How owners authenticate this connector.')\n .optional(),\n sync: z\n .enum(['5m', '15m', '1h', '6h', '24h'])\n .describe('How often Tappify asks your server to sync.')\n .optional(),\n metrics: z\n .array(\n z\n .object({\n key: z\n .string()\n .regex(SNAKE_CASE)\n .describe('The metric key. A series names it to draw its line.'),\n label: z\n .string()\n .min(1)\n .max(60)\n .describe('The label owners read on the metric.'),\n unit: z\n .enum(['count', 'ratio', 'currency', 'seconds', 'bytes'])\n .describe('How the value is formatted.'),\n kind: z\n .enum(['gauge', 'counter'])\n .describe(\n 'Whether the value is a level at a point in time (`gauge`) or accumulates (`counter`).',\n ),\n dimensions: z\n .array(\n z\n .object({\n key: z\n .string()\n .regex(SNAKE_CASE)\n .describe('The dimension key.'),\n label: z\n .string()\n .min(1)\n .max(60)\n .describe('The label owners read on the dimension.'),\n })\n .strict(),\n )\n .max(10)\n .describe('The breakdowns this metric can be split by.')\n .optional(),\n backfillDays: z\n .number()\n .int()\n .min(1)\n .max(90)\n .describe('How many days of history to fetch on the first sync.')\n .optional(),\n })\n .strict(),\n )\n .max(50)\n .describe(\n 'The metrics this connector writes into the metrics store. Needs the `metrics:write` scope.',\n )\n .optional(),\n })\n .strict();\n\nconst webhookContribution = z\n .object({\n event: z\n .string()\n .regex(DOMAIN_EVENT)\n .describe('The event name your server posts, as `domain.thing`.'),\n as: z\n .enum(['alert', 'banner', 'event'])\n .describe(\n 'What Tappify turns it into: an inbox `alert`, a page `banner`, or an `event` a chart marker and `tap.data.subscribe` can read.',\n ),\n payload: jsonSchema.describe(\n 'The JSON Schema for the body your server posts. An alert or a banner has to carry `why`.',\n ),\n })\n .strict();\n\nconst aiContribution = z\n .object({\n tools: z\n .array(\n z\n .object({\n id: contributionId,\n description: z\n .string()\n .min(10)\n .max(500)\n .describe(\n 'What this tool does, in the words the assistant reads when it decides whether to call it.',\n ),\n input: aiInputSchema.describe(\n 'The JSON Schema for the arguments the assistant passes. The assistant reads the `title` and `description` strings inside it, so they are scanned at publish and the whole schema is bounded at 16 KB.',\n ),\n returns: z\n .enum(['comparison', 'table', 'series', 'list', 'value'])\n .describe('The card the host draws from the result.'),\n cost: z\n .enum(['low', 'medium', 'high'])\n .describe(\n 'Roughly what one call costs to serve, so the assistant can prefer a cheaper tool.',\n ),\n cache: z\n .enum(['1m', '5m', '1h'])\n .describe(\n 'How long an identical call may be served from cache instead of reaching your server.',\n )\n .optional(),\n })\n .strict(),\n )\n .max(15, 'An extension declares at most 15 assistant tools.')\n .describe(\n 'The tools the assistant may call on your server. Needs the `ai:tools` scope.',\n )\n .optional(),\n mentions: z\n .array(\n z\n .object({\n id: contributionId,\n label: title,\n list: z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The path that answers the picker with `{ items: [{ id, label }] }`. The host always calls `/tappify/mentions/<id>` under `server.baseUrl` — the route `createTappifyHandler` serves — so write that path here; it records the route, it does not choose it.',\n ),\n })\n .strict(),\n )\n .max(10)\n .describe(\n 'The things owners can name with @ in chat, and where to list them. Needs the `ai:tools` scope.',\n )\n .optional(),\n actions: z\n .array(\n z\n .object({\n id: contributionId,\n description: z\n .string()\n .min(10)\n .max(500)\n .describe(\n 'What this action does, in the words the owner will read on the approval card.',\n ),\n input: aiInputSchema.describe(\n 'The JSON Schema for the arguments the assistant passes. The assistant reads the `title` and `description` strings inside it, so they are scanned at publish and the whole schema is bounded at 16 KB.',\n ),\n approval: z\n .literal(true)\n .describe(\n 'Always true. An action goes through an owner approval; there is no way to opt out.',\n ),\n reversible: z\n .boolean()\n .describe('Whether the owner can undo it after it runs.'),\n scope: z\n .enum(SCOPE_KEYS)\n .describe(\n 'The scope this action needs. Declare it under `scopes` as well.',\n ),\n estimatesCost: z\n .boolean()\n .describe(\n 'Whether the approval card will show what the action costs.',\n )\n .optional(),\n })\n .strict(),\n )\n .max(10, 'An extension declares at most 10 assistant actions.')\n .describe(\n \"The actions the assistant may run, each behind an owner approval. Needs the `ai:actions` scope, and each action's own scope. `tap.actions.run` creates the run and resolves with it; the host draws the approval card and calls your server only once the owner approves.\",\n )\n .optional(),\n knowledge: z\n .array(\n z\n .object({\n id: contributionId,\n category: z\n .enum(['metric_definitions', 'gotchas', 'setup'])\n .describe('What kind of knowledge the file carries.'),\n file: z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The markdown file in the bundle, as a path relative to the manifest.',\n ),\n })\n .strict(),\n )\n .max(20)\n .describe(\n 'Markdown the assistant indexes and cites when it answers about this extension.',\n )\n .optional(),\n skills: z\n .array(\n z\n .object({\n id: contributionId,\n name: z\n .string()\n .min(1)\n .max(60)\n .describe('The skill name the assistant matches on.'),\n description: z\n .string()\n .min(10)\n .max(300)\n .describe('When the assistant should reach for this skill.'),\n file: z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The markdown file in the bundle, as a path relative to the manifest.',\n ),\n tools: z\n .array(z.string())\n .max(15)\n .describe('The tool ids this skill is allowed to use.')\n .optional(),\n })\n .strict(),\n )\n .max(5, 'An extension declares at most 5 skills.')\n .describe(\n 'Markdown procedures that shape how the assistant works. Needs the `ai:skills` scope.',\n )\n .optional(),\n prompts: z\n .array(\n z\n .object({\n id: contributionId,\n title,\n template: z\n .string()\n .min(1)\n .max(2000)\n .describe(\n 'The prompt text, with `{{project.*}}`, `{{context.*}}` and `{{input.*}}` placeholders. The owner sees it resolved before it is sent.',\n ),\n surfaces: z\n .array(\n z.enum(['chat_suggestion', 'widget', 'page', 'result_card']),\n )\n .min(1)\n .describe('Where the host draws this prompt as a chip.'),\n after: z\n .array(z.string())\n .max(15)\n .describe(\n \"The tool ids whose results this prompt follows up on. They have to be this extension's own.\",\n )\n .optional(),\n input: jsonSchema\n .describe(\n 'The JSON Schema for a short form the owner fills in before the prompt is sent.',\n )\n .optional(),\n })\n .strict(),\n )\n .max(20, 'An extension declares at most 20 prompt templates.')\n .describe(\n 'Prompt templates the host offers owners as chips. Needs the `ai:skills` scope.',\n )\n .optional(),\n context: z\n .array(\n z\n .object({\n id: contributionId,\n description: z\n .string()\n .min(10)\n .max(300)\n .describe('What context this provider adds to the turn.'),\n when: z\n .array(z.enum(['turn_start', 'mention']))\n .min(1)\n .describe('When the host calls your server for it.'),\n cache: z\n .enum(['1m', '5m', '1h'])\n .describe('How long the context may be reused before refetching.')\n .optional(),\n })\n .strict(),\n )\n .max(3, 'An extension declares at most 3 context providers.')\n .describe(\n 'Providers the host calls to put your data in front of the assistant. Needs the `ai:skills` scope.',\n )\n .optional(),\n })\n .strict();\n\nconst statusNames = (stage: string) =>\n z.array(z.string()).describe(`Your own status names that mean ${stage}.`);\n\nconst workContribution = z\n .object({\n kind: z\n .enum(['tracker', 'docs', 'chat'])\n .describe('What kind of destination this is.'),\n container: z\n .object({\n label: z\n .string()\n .min(1)\n .max(60)\n .describe(\n 'What you call a container, such as \"Project\", \"Space\" or \"Channel\". Owners read it when they pick one.',\n ),\n list: z\n .string()\n .min(1)\n .describe(\n 'The path under `server.baseUrl` that lists the containers an owner can choose from.',\n ),\n })\n .strict()\n .describe('Where items land, and how owners pick one.'),\n creates: z\n .array(z.enum(['issue', 'page', 'message']))\n .min(1)\n .describe('What this destination can create.'),\n fields: z\n .object({\n priority: z\n .array(z.string())\n .describe('Your own priority names, highest first.')\n .optional(),\n labels: z.boolean().describe('Whether items carry labels.').optional(),\n assignee: z\n .boolean()\n .describe('Whether items carry an assignee.')\n .optional(),\n required: z\n .array(z.string())\n .describe(\n 'The fields an owner has to fill in before you can create.',\n ),\n })\n .strict()\n .describe('What a created item carries.'),\n attachments: z\n .array(z.enum(['image', 'link']))\n .describe('What can be attached to a created item.'),\n comments: z\n .enum(['two-way', 'none'])\n .describe('Whether comments sync back and forth or not at all.'),\n status: z\n .object({\n webhook: z\n .string()\n .regex(DOMAIN_EVENT)\n .describe('The event your server posts when an item changes status.'),\n map: z\n .object({\n open: statusNames('not started'),\n in_progress: statusNames('under way'),\n done: statusNames('finished'),\n })\n .strict()\n .describe(\n 'Your status names, grouped into the three stages Tappify draws.',\n ),\n })\n .strict()\n .describe('How status travels back from your system.')\n .optional(),\n identity: z\n .enum(['user', 'install'])\n .describe(\n 'Whether items are created as the owner who asked (`user`) or as the install itself.',\n ),\n })\n .strict();\n\n// Every field is optional so a vendor can fill the listing one value at a time;\n// the public-visibility refinement below requires all five before publishing.\nconst listing = z\n .object({\n longDescription: z\n .string()\n .min(1)\n .max(5000)\n .describe('The body of your listing page, in markdown.')\n .optional(),\n screenshots: z\n .array(z.string().min(1))\n .min(1, 'A listing shows 1 to 6 screenshots.')\n .max(6, 'A listing shows 1 to 6 screenshots.')\n .describe(\n 'Screenshot files carried in the bundle, as paths relative to the manifest. png at 1600 by 1000.',\n )\n .optional(),\n website: httpsUrl\n .describe('Where owners go to learn more about this extension.')\n .optional(),\n supportUrl: httpsUrl\n .describe('Where owners go when something breaks.')\n .optional(),\n privacyUrl: httpsUrl\n .describe(\n 'Your privacy policy, linked from the consent screen before anyone installs.',\n )\n .optional(),\n })\n .strict();\n\nconst LISTING_FIELDS = [\n 'longDescription',\n 'screenshots',\n 'website',\n 'supportUrl',\n 'privacyUrl',\n] as const;\n\nconst procedureDeclaration = z\n .object({\n input: jsonSchema.describe(\n 'The JSON Schema for the argument your handler receives. The host validates against it before calling.',\n ),\n output: jsonSchema.describe(\n 'The JSON Schema for what your handler returns.',\n ),\n cache: z\n .enum(['1m', '5m', '1h'])\n .describe(\n 'How long an identical call may be served from cache instead of reaching your server.',\n )\n .optional(),\n kind: z\n .enum(['read', 'write'])\n .describe('Whether this procedure only reads or also changes something.')\n .optional(),\n })\n .strict();\n\nconst manifestObject = z\n .object({\n $schema: z\n .string()\n .describe(\n 'The URL of this JSON Schema. Editors read it for completion and inline validation.',\n )\n .optional(),\n id: z\n .string()\n .regex(SLUG)\n .describe(\n 'The identifier for this extension, unique across the Tap Store and fixed after the first publish.',\n ),\n name: z\n .string()\n .min(2)\n .max(40)\n .describe(\n 'The name owners see in the Tap Store and on every surface this extension renders.',\n ),\n description: z\n .string()\n .min(10)\n .max(200)\n .describe(\n 'The one line owners read on the Tap Store card and on the install screen.',\n ),\n icon: z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The icon file carried in the bundle, as a path relative to the manifest. Square png or svg, at most 512 KB.',\n ),\n category: z\n .enum(CATEGORIES, {\n error:\n 'Choose a category from the Tap Store list (see the Categories reference).',\n })\n .describe('The Tap Store section owners browse and filter by.'),\n sdk: z\n .string()\n .min(1)\n .max(60)\n .describe(\n 'The SDK range this build targets, such as \"^2.0.0\". The host serves one major at a time and rejects a range that excludes it.',\n ),\n visibility: z\n .enum(['public', 'unlisted', 'private'])\n .describe(\n 'Who can find and install this extension. \"public\" lists it in the Tap Store and needs all five listing fields; \"unlisted\" installs by link; \"private\" installs only in your own workspace.',\n ),\n installScope: z\n .enum(['project', 'organization'])\n .describe(\n 'Whether one install belongs to a single project or to a whole organization. An organization install contributes no pages, tabs, widgets or series, because those render on a project.',\n ),\n scopes: z\n .array(scopeEntry)\n .min(1)\n .max(SCOPE_KEYS.length)\n .describe(\n 'The permissions an owner grants at install. Declare only what your contributions use.',\n ),\n contributes: z\n .object({\n pages: z\n .array(pageContribution)\n .max(20)\n .describe(\n \"Pages of this extension's own, each on its own route under the project. `nav: true` also puts one in the project navigation.\",\n )\n .optional(),\n tabs: z\n .array(tabContribution)\n .max(20)\n .describe(\"Tabs beside the host's own on a named host page.\")\n .optional(),\n widgets: z\n .array(widgetContribution)\n .max(20)\n .describe('Cards drawn into a named slot on a host page.')\n .optional(),\n series: z\n .array(seriesContribution)\n .max(20)\n .describe(\n 'Lines drawn onto a host chart from a metric the connector writes. The host draws the line on the chart this names and puts the vendor in the chart legend; a stale metric is drawn dashed with the time of its last sync.',\n )\n .optional(),\n banners: z\n .array(bannerContribution)\n .max(20)\n .describe(\n 'Banners raised on a host page by a webhook declared with as: \"banner\". The host draws one at the top of that page, carrying the `why` sentence from the delivery, until the owner dismisses it.',\n )\n .optional(),\n rowActions: z\n .array(rowActionContribution)\n .max(20)\n .describe('Actions offered on a row of a host table.')\n .optional(),\n markers: z\n .array(markerContribution)\n .max(20)\n .describe(\n 'Markers drawn on a host chart by a webhook declared with as: \"event\". Needs the `ui:render` and `alerts:write` scopes. The host draws the glyph on the chart time axis at the moment the delivery arrived, with the vendor named on hover.',\n )\n .optional(),\n settings: settingsContribution\n .describe('The settings panel owners open for this install.')\n .optional(),\n storage: z\n .record(z.string().regex(SNAKE_CASE), storageCollection)\n .describe(\n 'The hosted storage collections this extension reads and writes. At most 10, and the name \"settings\" is reserved. Needs the `storage:write` scope.',\n )\n .optional(),\n connector: connectorContribution\n .describe(\n 'How this extension authenticates against an outside service, how often it syncs, and the metrics it writes.',\n )\n .optional(),\n webhooks: z\n .array(webhookContribution)\n .max(20)\n .describe(\n \"The events this extension's server posts to Tappify, and what each one becomes. An `alert` or a `banner` needs the `alerts:write` scope.\",\n )\n .optional(),\n ai: aiContribution\n .describe(\n 'What this extension offers the assistant: tools, mentions, actions, knowledge, skills, prompts and context providers.',\n )\n .optional(),\n work: workContribution\n .describe(\n 'The work destination this extension creates issues, pages or messages in. Needs the `work:create` and `work:sync` scopes.',\n )\n .optional(),\n })\n .strict()\n .describe(\n 'What this extension adds to the host. At most 20 UI contributions in total, and a page, tab, widget, row action, marker or settings panel needs the `ui:render` scope.',\n ),\n server: z\n .object({\n baseUrl: httpsUrl.describe(\n 'Where the host calls your server. `<baseUrl>/tappify/health` answers 200 within three seconds at publish.',\n ),\n sandboxBaseUrl: httpsUrl\n .describe(\n 'Where dev installs and sandbox projects call instead. Defaults to `baseUrl`.',\n )\n .optional(),\n procedures: z\n .record(\n z\n .string()\n .regex(\n PROCEDURE_NAME,\n 'Procedure names are identifiers such as \"getSummary\" or \"get-summary\": letters, digits, _ and -, starting with a letter, at most 60 characters. They become URL segments and generated type names.',\n ),\n procedureDeclaration,\n )\n .describe(\n 'The procedures the host may call on your server, by name, each with its input and output schema. At most 30.',\n )\n .optional(),\n events: z\n .array(\n z.enum(EVENTS, {\n error: issue =>\n `\"${String(issue.input)}\" is not a Tappify event. Subscribe to a name from the event catalogue.`,\n }),\n )\n .describe(\n 'The Tappify events your server subscribes to. Each is delivered to `POST /tappify/events` as it happens, with the same body every host-to-vendor call carries.',\n )\n .optional(),\n })\n .strict()\n .describe(\n 'Your own server: where the host calls it, and what it answers. Required once you declare a connector, a webhook, an assistant contribution, a work destination or a procedure.',\n )\n .optional(),\n telemetry: z\n .array(\n z\n .string()\n .regex(\n SNAKE_CASE,\n 'Telemetry names are snake_case, such as \"funnel_viewed\".',\n )\n .max(60),\n )\n .max(20)\n .describe(\n 'The telemetry names this extension may report. `tap.telemetry.event` rejects a name that is not declared here.',\n )\n .optional(),\n listing: listing\n .describe(\n 'What owners read on your Tap Store listing page. A public extension needs all five fields.',\n )\n .optional(),\n pricing: z\n .never()\n .describe(\n 'Reserved. Tappify sets pricing outside the manifest, so a manifest that carries this field fails validation.',\n )\n .optional(),\n score: z\n .never()\n .describe(\n 'Reserved. Tappify computes the quality score, so a manifest that carries this field fails validation.',\n )\n .optional(),\n })\n .strict();\n\nexport function sdkMajors(range: string): number[] {\n const majors = new Set<number>();\n for (const alternative of range.split('||')) {\n for (const token of alternative.trim().split(/\\s+/)) {\n if (token.startsWith('<')) continue;\n const match = /^(?:\\^|~|>=|>|=)?\\s*(\\d+)/.exec(token);\n if (match) majors.add(Number(match[1]));\n }\n }\n return [...majors].sort((left, right) => left - right);\n}\n\nfunction uiCount(contributes: ExtensionManifest['contributes']): number {\n return (\n (contributes.pages?.length ?? 0) +\n (contributes.tabs?.length ?? 0) +\n (contributes.widgets?.length ?? 0) +\n (contributes.series?.length ?? 0) +\n (contributes.banners?.length ?? 0) +\n (contributes.rowActions?.length ?? 0) +\n (contributes.markers?.length ?? 0) +\n (contributes.settings ? 1 : 0)\n );\n}\n\nfunction hasRenderedSurface(\n contributes: ExtensionManifest['contributes'],\n): boolean {\n return (\n (contributes.pages?.length ?? 0) > 0 ||\n (contributes.tabs?.length ?? 0) > 0 ||\n (contributes.widgets?.length ?? 0) > 0 ||\n (contributes.rowActions?.length ?? 0) > 0 ||\n (contributes.markers?.length ?? 0) > 0 ||\n contributes.settings !== undefined\n );\n}\n\nexport const manifestSchema = manifestObject.superRefine((manifest, ctx) => {\n const granted = new Set<string>(manifest.scopes.map(scope => scope.key));\n const contributes = manifest.contributes;\n\n const requireScope = (\n key: string,\n path: (string | number)[],\n because: string,\n ): void => {\n if (granted.has(key)) return;\n ctx.addIssue({\n code: 'custom',\n path,\n message: `Add the ${key} scope: ${because}`,\n });\n };\n\n for (const [index, widget] of (contributes.widgets ?? []).entries()) {\n const slots = slotsForPage(widget.page);\n if (!slots.includes(widget.slot)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'widgets', index, 'slot'],\n message: `Slot \"${widget.slot}\" is not on the ${widget.page} page. Pick one of: ${slots.join(', ')}.`,\n });\n }\n }\n\n if (hasRenderedSurface(contributes)) {\n requireScope(\n 'ui:render',\n ['scopes'],\n 'this extension renders UI in the host.',\n );\n }\n\n if (uiCount(contributes) > 20) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes'],\n message:\n 'An extension declares at most 20 UI contributions. Remove some, or split the extension in two.',\n });\n }\n\n if (contributes.storage) {\n requireScope(\n 'storage:write',\n ['scopes'],\n 'this extension declares hosted storage collections.',\n );\n if (Object.keys(contributes.storage).length > 10) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'storage'],\n message: 'An extension declares at most 10 storage collections.',\n });\n }\n if ('settings' in contributes.storage) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'storage', 'settings'],\n message:\n 'The collection name \"settings\" is reserved for the settings contribution. Rename this collection.',\n });\n }\n }\n\n if (contributes.connector?.metrics?.length) {\n requireScope(\n 'metrics:write',\n ['scopes'],\n 'the connector writes metrics into the metrics store.',\n );\n }\n\n const webhookEventsAs = (as: 'alert' | 'banner' | 'event'): Set<string> =>\n new Set(\n (contributes.webhooks ?? [])\n .filter(webhook => webhook.as === as)\n .map(webhook => webhook.event),\n );\n\n const bannerEvents = webhookEventsAs('banner');\n const markerEvents = webhookEventsAs('event');\n\n if (\n (contributes.webhooks ?? []).some(\n webhook => webhook.as === 'alert' || webhook.as === 'banner',\n )\n ) {\n requireScope(\n 'alerts:write',\n ['scopes'],\n 'a declared webhook becomes an alert or a banner.',\n );\n }\n\n for (const [index, banner] of (contributes.banners ?? []).entries()) {\n if (!bannerEvents.has(banner.event)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'banners', index, 'event'],\n message: `Event \"${banner.event}\" is not declared under webhooks. Declare it with as: \"banner\".`,\n });\n }\n }\n\n for (const [index, marker] of (contributes.markers ?? []).entries()) {\n if (!markerEvents.has(marker.event)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'markers', index, 'event'],\n message: `Event \"${marker.event}\" is not declared under webhooks. Declare it with as: \"event\".`,\n });\n }\n }\n\n if ((contributes.markers?.length ?? 0) > 0) {\n requireScope('ui:render', ['scopes'], 'markers draw on a host chart.');\n requireScope(\n 'alerts:write',\n ['scopes'],\n 'markers come from declared webhooks.',\n );\n }\n\n const metricKeys = new Set(\n (contributes.connector?.metrics ?? []).map(metric => metric.key),\n );\n\n for (const [index, series] of (contributes.series ?? []).entries()) {\n if (!metricKeys.has(series.metric)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'series', index, 'metric'],\n message: `Metric \"${series.metric}\" is not declared by the connector. Add it under contributes.connector.metrics.`,\n });\n }\n }\n\n const ai = contributes.ai;\n if (ai?.tools?.length || ai?.mentions?.length) {\n requireScope('ai:tools', ['scopes'], 'the assistant calls declared tools.');\n }\n if (ai?.actions?.length) {\n requireScope(\n 'ai:actions',\n ['scopes'],\n 'declared actions run behind approval.',\n );\n for (const [index, action] of ai.actions.entries()) {\n if (!granted.has(action.scope)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'ai', 'actions', index, 'scope'],\n message: `Add the ${action.scope} scope: the action \"${action.id}\" declares it.`,\n });\n }\n }\n }\n if (ai?.skills?.length || ai?.prompts?.length || ai?.context?.length) {\n requireScope(\n 'ai:skills',\n ['scopes'],\n 'skills, prompts and context providers shape the assistant.',\n );\n }\n\n const toolIds = new Set((ai?.tools ?? []).map(tool => tool.id));\n for (const [index, prompt] of (ai?.prompts ?? []).entries()) {\n for (const [afterIndex, toolId] of (prompt.after ?? []).entries()) {\n if (!toolIds.has(toolId)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'ai', 'prompts', index, 'after', afterIndex],\n message: `Tool \"${toolId}\" is not declared by this extension. Use one of your own tool ids.`,\n });\n }\n }\n }\n\n if (contributes.work) {\n requireScope(\n 'work:create',\n ['scopes'],\n 'the work destination creates items.',\n );\n requireScope(\n 'work:sync',\n ['scopes'],\n 'the work destination syncs comments and status.',\n );\n }\n\n const needsServer =\n contributes.connector !== undefined ||\n (contributes.webhooks?.length ?? 0) > 0 ||\n ai !== undefined ||\n contributes.work !== undefined ||\n manifest.server?.procedures !== undefined;\n\n if (needsServer && !manifest.server?.baseUrl) {\n ctx.addIssue({\n code: 'custom',\n path: ['server', 'baseUrl'],\n message:\n 'Set server.baseUrl: connectors, webhooks, assistant contributions, work destinations and procedures all call your server.',\n });\n }\n\n const procedures = manifest.server?.procedures ?? {};\n if (Object.keys(procedures).length > 30) {\n ctx.addIssue({\n code: 'custom',\n path: ['server', 'procedures'],\n message: 'An extension declares at most 30 procedures.',\n });\n }\n\n for (const scope of manifest.scopes) {\n const definition = findScope(scope.key);\n if (definition?.requiresJustification && !scope.justification) {\n ctx.addIssue({\n code: 'custom',\n path: ['scopes'],\n message: `${scope.key} needs a justification of 20 to 500 characters that tells the owner why you ask for it.`,\n });\n }\n }\n\n if (manifest.visibility === 'public') {\n const missing = LISTING_FIELDS.filter(\n field => manifest.listing?.[field] === undefined,\n );\n if (missing.length > 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['listing'],\n message: `A public extension needs a complete listing: add ${missing.join(', ')}. Set them with \\`tappify extension set listing.<field> <value>\\`, or keep visibility \"unlisted\" or \"private\" until the listing is ready.`,\n });\n }\n }\n\n if (manifest.installScope === 'organization') {\n const forbidden = (['pages', 'tabs', 'widgets', 'series'] as const).filter(\n key => (contributes[key]?.length ?? 0) > 0,\n );\n if (forbidden.length > 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['installScope'],\n message: `Organization-scoped extensions cannot contribute widgets, tabs, pages or series, because those render on a project. Remove ${forbidden.join(', ')}, or set installScope to \"project\".`,\n });\n }\n }\n\n if (\n !sdkMajors(manifest.sdk).some(major => SERVED_SDK_MAJORS.includes(major))\n ) {\n ctx.addIssue({\n code: 'custom',\n path: ['sdk'],\n message: `The host serves SDK major ${SERVED_SDK_MAJORS.join(', ')}. Set sdk to a range that includes it, such as \"^2.0.0\".`,\n });\n }\n});\n\nexport function manifestJsonSchema(): Record<string, unknown> {\n const { $schema, ...generated } = z.toJSONSchema(manifestObject, {\n target: 'draft-2020-12',\n });\n return {\n $schema,\n $id: 'https://schema.tappify.ai/extension/v2.json',\n title: 'Tappify extension manifest',\n description:\n 'The tappify.extension.json file at the root of an extension repository.',\n ...generated,\n };\n}\n\n/**\n * A record whose key schema rejects reports only \"Invalid key in record\", so the\n * key schema's own message is lifted out of the nested issue.\n */\nfunction issueMessage(issue: z.core.$ZodIssue): string {\n if (issue.code !== 'invalid_key') return issue.message;\n return issue.issues[0]?.message ?? issue.message;\n}\n\nexport function validateManifest(value: unknown): ManifestValidation {\n const result = manifestSchema.safeParse(value);\n if (result.success) {\n return { ok: true, manifest: result.data };\n }\n\n const issues: ManifestIssue[] = result.error.issues.map(issue => ({\n path: issue.path.length > 0 ? issue.path.join('.') : '(root)',\n message: issueMessage(issue),\n }));\n\n return { ok: false, issues };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAMA,IAAM,gBAAoE;AAAA,EACxE,MAAM;AAAA,EACN,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,WAAW;AACb;AAEO,SAAS,eAAe,UAAgD;AAC7E,SAAO;AACT;AAOO,SAAS,WAAW,MAAqB,IAAqB;AACnE,MAAI,SAAS,WAAY,QAAO;AAChC,SAAO,KAAK,cAAc,IAAI,CAAC,IAAI,MAAM,EAAE;AAC7C;AAEO,SAAS,WAAW,aAA6B;AACtD,SAAO,OAAO,YAAY,QAAQ,MAAM,GAAG,CAAC;AAC9C;AAEO,SAAS,gBACd,UACqB;AACrB,QAAM,cAAc,SAAS;AAC7B,QAAM,OAA4B,CAAC;AAEnC,aAAW,QAAQ,YAAY,SAAS,CAAC,GAAG;AAC1C,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,IAAI,KAAK;AAAA,MACT,OAAO,KAAK;AAAA,MACZ,QAAQ,WAAW,QAAQ,KAAK,EAAE;AAAA,IACpC,CAAC;AAAA,EACH;AACA,aAAW,OAAO,YAAY,QAAQ,CAAC,GAAG;AACxC,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,IAAI,IAAI;AAAA,MACR,OAAO,IAAI;AAAA,MACX,QAAQ,WAAW,OAAO,IAAI,EAAE;AAAA,IAClC,CAAC;AAAA,EACH;AACA,aAAW,UAAU,YAAY,WAAW,CAAC,GAAG;AAC9C,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,IAAI,OAAO;AAAA,MACX,OAAO,OAAO;AAAA,MACd,QAAQ,WAAW,UAAU,OAAO,EAAE;AAAA,IACxC,CAAC;AAAA,EACH;AACA,aAAW,aAAa,YAAY,cAAc,CAAC,GAAG;AACpD,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,IAAI,UAAU;AAAA,MACd,OAAO,UAAU;AAAA,MACjB,QAAQ,WAAW,aAAa,UAAU,EAAE;AAAA,IAC9C,CAAC;AAAA,EACH;AACA,MAAI,YAAY,UAAU;AACxB,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,OAAO,YAAY,SAAS;AAAA,MAC5B,QAAQ,WAAW,UAAU;AAAA,IAC/B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;AC/EA,SAAS,SAAS;AAmBlB,IAAM,OAAO;AACb,IAAM,kBAAkB;AACxB,IAAM,aAAa;AACnB,IAAM,iBAAiB;AACvB,IAAM,eAAe;AAErB,IAAM,aAAa,EAChB,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAC9B;AAAA,EACC;AACF;AAGK,IAAM,4BAA4B;AAGlC,IAAM,qBAAqB;AAElC,IAAM,sBAAsB,oBAAI,IAAI,CAAC,SAAS,aAAa,CAAC;AAC5D,IAAM,sBAAsB;AAE5B,SAAS,mBAAmB,OAAgB,QAAQ,GAAkB;AACpE,MAAI,QAAQ,oBAAqB,QAAO;AAExC,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,eAAW,SAAS,OAAO;AACzB,YAAM,QAAQ,mBAAmB,OAAO,QAAQ,CAAC;AACjD,UAAI,UAAU,KAAM,QAAO;AAAA,IAC7B;AACA,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AAExD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QACE,oBAAoB,IAAI,GAAG,KAC3B,OAAO,UAAU,YACjB,MAAM,SAAS,oBACf;AACA,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,mBAAmB,OAAO,QAAQ,CAAC;AACjD,QAAI,UAAU,KAAM,QAAO;AAAA,EAC7B;AAEA,SAAO;AACT;AAQA,IAAM,gBAAgB,WAAW,YAAY,CAAC,OAAO,QAAQ;AAC3D,QAAM,OAAO,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,KAAK,CAAC,EAAE;AAC7D,MAAI,OAAO,2BAA2B;AACpC,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,SAAS,mCAAmC,IAAI,mCAAmC,yBAAyB;AAAA,IAC9G,CAAC;AAAA,EACH;AAEA,QAAM,WAAW,mBAAmB,KAAK;AACzC,MAAI,aAAa,MAAM;AACrB,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,SAAS,MAAM,QAAQ,6CAA6C,kBAAkB;AAAA,IACxF,CAAC;AAAA,EACH;AACF,CAAC;AAED,IAAM,WAAW,EAAE,IAAI,EAAE,WAAW,UAAU;AAC9C,IAAM,YAAY,EACf,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAG,EACP;AAAA,EACC;AACF;AACF,IAAM,iBAAiB,EACpB,OAAO,EACP,MAAM,eAAe,EACrB;AAAA,EACC;AACF;AACF,IAAM,QAAQ,EACX,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,6CAA6C;AAEzD,IAAM,aAAa,EAChB,OAAO;AAAA,EACN,KAAK,EACF,KAAK,YAAY;AAAA,IAChB,OAAO;AAAA,EACT,CAAC,EACA,SAAS,2CAA2C;AAAA,EACvD,eAAe,EACZ,OAAO,EACP,IAAI,EAAE,EACN,IAAI,GAAG,EACP;AAAA,IACC;AAAA,EACF,EACC,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,SAAS,EACZ,QAAQ,EACR;AAAA,EACC;AACF,EACC,SAAS;AAEZ,IAAM,WAAW,CAAC,SAAiB,EAAE,KAAK,SAAS,EAAE,SAAS,IAAI;AAElE,IAAM,mBAAmB,EACtB,OAAO;AAAA,EACN,IAAI;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,EACP,KAAK,EACF,QAAQ,EACR;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ;AACF,CAAC,EACA,OAAO;AAEV,IAAM,kBAAkB,EACrB,OAAO;AAAA,EACN,IAAI;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,EACP,MAAM,SAAS,iCAAiC;AAAA,EAChD;AACF,CAAC,EACA,OAAO;AAEV,IAAM,qBAAqB,EACxB,OAAO;AAAA,EACN,IAAI;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,EACP,MAAM,SAAS,uCAAuC;AAAA,EACtD,MAAM,EACH,KAAK,KAAK,EACV;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EACH,KAAK,CAAC,OAAO,OAAO,KAAK,CAAC,EAC1B;AAAA,IACC;AAAA,EACF;AAAA,EACF,YAAY,EACT,QAAQ,EACR;AAAA,IACC;AAAA,EACF,EACC,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,qBAAqB,EACxB,OAAO;AAAA,EACN,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,OAAO,EAAE,KAAK,MAAM,EAAE,SAAS,uCAAuC;AAAA,EACtE,QAAQ,EACL,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN;AAAA,IACC;AAAA,EACF;AACJ,CAAC,EACA,OAAO;AAEV,IAAM,qBAAqB,EACxB,OAAO;AAAA,EACN,IAAI;AAAA,EACJ,MAAM,SAAS,uCAAuC;AAAA,EACtD,OAAO,EACJ,OAAO,EACP,MAAM,YAAY,EAClB;AAAA,IACC;AAAA,EACF;AACJ,CAAC,EACA,OAAO;AAEV,IAAM,wBAAwB,EAC3B,OAAO;AAAA,EACN,IAAI;AAAA,EACJ;AAAA,EACA,OAAO,EACJ,KAAK,UAAU,EACf,SAAS,iDAAiD;AAAA,EAC7D,OAAO;AACT,CAAC,EACA,OAAO;AAEV,IAAM,qBAAqB,EACxB,OAAO;AAAA,EACN,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,OAAO,EACJ,MAAM,CAAC,EAAE,KAAK,MAAM,GAAG,EAAE,QAAQ,GAAG,CAAC,CAAC,EACtC;AAAA,IACC;AAAA,EACF;AAAA,EACF,OAAO,EACJ,OAAO,EACP,MAAM,YAAY,EAClB;AAAA,IACC;AAAA,EACF;AAAA,EACF,OAAO,EACJ,OAAO,EACP,IAAI,CAAC,EACL,IAAI,CAAC,EACL,SAAS,gDAAgD;AAC9D,CAAC,EACA,OAAO;AAEV,IAAM,uBAAuB,EAC1B,OAAO;AAAA,EACN,OAAO;AAAA,EACP,QAAQ,WAAW;AAAA,IACjB;AAAA,EACF;AACF,CAAC,EACA,OAAO;AAEV,IAAM,oBAAoB,EACvB,OAAO;AAAA,EACN,OAAO,EACJ,KAAK,CAAC,QAAQ,WAAW,cAAc,CAAC,EACxC;AAAA,IACC;AAAA,EACF;AAAA,EACF,WAAW,EACR,QAAQ,EACR;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,QAAQ,WAAW;AAAA,IACjB;AAAA,EACF;AACF,CAAC,EACA,OAAO;AAEV,IAAM,kBAAkB,EACrB,OAAO;AAAA,EACN,MAAM,EACH,OAAO,EACP,MAAM,UAAU,EAChB,SAAS,+CAA+C;AAAA,EAC3D,OAAO,EACJ,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,wCAAwC;AAAA,EACpD,MAAM,EACH,KAAK,CAAC,QAAQ,YAAY,YAAY,QAAQ,QAAQ,CAAC,EACvD,SAAS,sCAAsC;AAAA,EAClD,UAAU,EACP,QAAQ,EACR,SAAS,6DAA6D;AAAA,EACzE,SAAS,EACN,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,4CAA4C,EACrD,SAAS;AAAA,EACZ,MAAM,EACH,OAAO,EACP,IAAI,GAAG,EACP,SAAS,iCAAiC,EAC1C,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,wBAAwB,EAC3B,OAAO;AAAA,EACN,MAAM,EACH,OAAO;AAAA,IACN,QAAQ,EACL,KAAK,CAAC,QAAQ,WAAW,OAAO,CAAC,EACjC,SAAS,mDAAmD;AAAA,IAC/D,QAAQ,EACL,MAAM,eAAe,EACrB,IAAI,EAAE,EACN;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,OAAO,EACJ,OAAO;AAAA,MACN,kBAAkB,SAAS;AAAA,QACzB;AAAA,MACF;AAAA,MACA,UAAU,SAAS;AAAA,QACjB;AAAA,MACF;AAAA,MACA,QAAQ,EACL,MAAM,EAAE,OAAO,CAAC,EAChB;AAAA,QACC;AAAA,MACF;AAAA,IACJ,CAAC,EACA,OAAO,EACP,SAAS,uCAAuC,EAChD,SAAS;AAAA,EACd,CAAC,EACA,OAAO,EACP,SAAS,yCAAyC,EAClD,SAAS;AAAA,EACZ,MAAM,EACH,KAAK,CAAC,MAAM,OAAO,MAAM,MAAM,KAAK,CAAC,EACrC,SAAS,6CAA6C,EACtD,SAAS;AAAA,EACZ,SAAS,EACN;AAAA,IACC,EACG,OAAO;AAAA,MACN,KAAK,EACF,OAAO,EACP,MAAM,UAAU,EAChB,SAAS,qDAAqD;AAAA,MACjE,OAAO,EACJ,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,sCAAsC;AAAA,MAClD,MAAM,EACH,KAAK,CAAC,SAAS,SAAS,YAAY,WAAW,OAAO,CAAC,EACvD,SAAS,6BAA6B;AAAA,MACzC,MAAM,EACH,KAAK,CAAC,SAAS,SAAS,CAAC,EACzB;AAAA,QACC;AAAA,MACF;AAAA,MACF,YAAY,EACT;AAAA,QACC,EACG,OAAO;AAAA,UACN,KAAK,EACF,OAAO,EACP,MAAM,UAAU,EAChB,SAAS,oBAAoB;AAAA,UAChC,OAAO,EACJ,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,yCAAyC;AAAA,QACvD,CAAC,EACA,OAAO;AAAA,MACZ,EACC,IAAI,EAAE,EACN,SAAS,6CAA6C,EACtD,SAAS;AAAA,MACZ,cAAc,EACX,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,sDAAsD,EAC/D,SAAS;AAAA,IACd,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,EAAE,EACN;AAAA,IACC;AAAA,EACF,EACC,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,sBAAsB,EACzB,OAAO;AAAA,EACN,OAAO,EACJ,OAAO,EACP,MAAM,YAAY,EAClB,SAAS,sDAAsD;AAAA,EAClE,IAAI,EACD,KAAK,CAAC,SAAS,UAAU,OAAO,CAAC,EACjC;AAAA,IACC;AAAA,EACF;AAAA,EACF,SAAS,WAAW;AAAA,IAClB;AAAA,EACF;AACF,CAAC,EACA,OAAO;AAEV,IAAM,iBAAiB,EACpB,OAAO;AAAA,EACN,OAAO,EACJ;AAAA,IACC,EACG,OAAO;AAAA,MACN,IAAI;AAAA,MACJ,aAAa,EACV,OAAO,EACP,IAAI,EAAE,EACN,IAAI,GAAG,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAO,cAAc;AAAA,QACnB;AAAA,MACF;AAAA,MACA,SAAS,EACN,KAAK,CAAC,cAAc,SAAS,UAAU,QAAQ,OAAO,CAAC,EACvD,SAAS,0CAA0C;AAAA,MACtD,MAAM,EACH,KAAK,CAAC,OAAO,UAAU,MAAM,CAAC,EAC9B;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAO,EACJ,KAAK,CAAC,MAAM,MAAM,IAAI,CAAC,EACvB;AAAA,QACC;AAAA,MACF,EACC,SAAS;AAAA,IACd,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,IAAI,mDAAmD,EAC3D;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,UAAU,EACP;AAAA,IACC,EACG,OAAO;AAAA,MACN,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAG,EACP;AAAA,QACC;AAAA,MACF;AAAA,IACJ,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,EAAE,EACN;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,SAAS,EACN;AAAA,IACC,EACG,OAAO;AAAA,MACN,IAAI;AAAA,MACJ,aAAa,EACV,OAAO,EACP,IAAI,EAAE,EACN,IAAI,GAAG,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAO,cAAc;AAAA,QACnB;AAAA,MACF;AAAA,MACA,UAAU,EACP,QAAQ,IAAI,EACZ;AAAA,QACC;AAAA,MACF;AAAA,MACF,YAAY,EACT,QAAQ,EACR,SAAS,8CAA8C;AAAA,MAC1D,OAAO,EACJ,KAAK,UAAU,EACf;AAAA,QACC;AAAA,MACF;AAAA,MACF,eAAe,EACZ,QAAQ,EACR;AAAA,QACC;AAAA,MACF,EACC,SAAS;AAAA,IACd,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,IAAI,qDAAqD,EAC7D;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,WAAW,EACR;AAAA,IACC,EACG,OAAO;AAAA,MACN,IAAI;AAAA,MACJ,UAAU,EACP,KAAK,CAAC,sBAAsB,WAAW,OAAO,CAAC,EAC/C,SAAS,0CAA0C;AAAA,MACtD,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAG,EACP;AAAA,QACC;AAAA,MACF;AAAA,IACJ,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,EAAE,EACN;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,QAAQ,EACL;AAAA,IACC,EACG,OAAO;AAAA,MACN,IAAI;AAAA,MACJ,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,0CAA0C;AAAA,MACtD,aAAa,EACV,OAAO,EACP,IAAI,EAAE,EACN,IAAI,GAAG,EACP,SAAS,iDAAiD;AAAA,MAC7D,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAG,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAO,EACJ,MAAM,EAAE,OAAO,CAAC,EAChB,IAAI,EAAE,EACN,SAAS,4CAA4C,EACrD,SAAS;AAAA,IACd,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,GAAG,yCAAyC,EAChD;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,SAAS,EACN;AAAA,IACC,EACG,OAAO;AAAA,MACN,IAAI;AAAA,MACJ;AAAA,MACA,UAAU,EACP,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAI,EACR;AAAA,QACC;AAAA,MACF;AAAA,MACF,UAAU,EACP;AAAA,QACC,EAAE,KAAK,CAAC,mBAAmB,UAAU,QAAQ,aAAa,CAAC;AAAA,MAC7D,EACC,IAAI,CAAC,EACL,SAAS,6CAA6C;AAAA,MACzD,OAAO,EACJ,MAAM,EAAE,OAAO,CAAC,EAChB,IAAI,EAAE,EACN;AAAA,QACC;AAAA,MACF,EACC,SAAS;AAAA,MACZ,OAAO,WACJ;AAAA,QACC;AAAA,MACF,EACC,SAAS;AAAA,IACd,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,IAAI,oDAAoD,EAC5D;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,SAAS,EACN;AAAA,IACC,EACG,OAAO;AAAA,MACN,IAAI;AAAA,MACJ,aAAa,EACV,OAAO,EACP,IAAI,EAAE,EACN,IAAI,GAAG,EACP,SAAS,8CAA8C;AAAA,MAC1D,MAAM,EACH,MAAM,EAAE,KAAK,CAAC,cAAc,SAAS,CAAC,CAAC,EACvC,IAAI,CAAC,EACL,SAAS,yCAAyC;AAAA,MACrD,OAAO,EACJ,KAAK,CAAC,MAAM,MAAM,IAAI,CAAC,EACvB,SAAS,uDAAuD,EAChE,SAAS;AAAA,IACd,CAAC,EACA,OAAO;AAAA,EACZ,EACC,IAAI,GAAG,oDAAoD,EAC3D;AAAA,IACC;AAAA,EACF,EACC,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,cAAc,CAAC,UACnB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,mCAAmC,KAAK,GAAG;AAE1E,IAAM,mBAAmB,EACtB,OAAO;AAAA,EACN,MAAM,EACH,KAAK,CAAC,WAAW,QAAQ,MAAM,CAAC,EAChC,SAAS,mCAAmC;AAAA,EAC/C,WAAW,EACR,OAAO;AAAA,IACN,OAAO,EACJ,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN;AAAA,MACC;AAAA,IACF;AAAA,IACF,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL;AAAA,MACC;AAAA,IACF;AAAA,EACJ,CAAC,EACA,OAAO,EACP,SAAS,4CAA4C;AAAA,EACxD,SAAS,EACN,MAAM,EAAE,KAAK,CAAC,SAAS,QAAQ,SAAS,CAAC,CAAC,EAC1C,IAAI,CAAC,EACL,SAAS,mCAAmC;AAAA,EAC/C,QAAQ,EACL,OAAO;AAAA,IACN,UAAU,EACP,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,yCAAyC,EAClD,SAAS;AAAA,IACZ,QAAQ,EAAE,QAAQ,EAAE,SAAS,6BAA6B,EAAE,SAAS;AAAA,IACrE,UAAU,EACP,QAAQ,EACR,SAAS,kCAAkC,EAC3C,SAAS;AAAA,IACZ,UAAU,EACP,MAAM,EAAE,OAAO,CAAC,EAChB;AAAA,MACC;AAAA,IACF;AAAA,EACJ,CAAC,EACA,OAAO,EACP,SAAS,8BAA8B;AAAA,EAC1C,aAAa,EACV,MAAM,EAAE,KAAK,CAAC,SAAS,MAAM,CAAC,CAAC,EAC/B,SAAS,yCAAyC;AAAA,EACrD,UAAU,EACP,KAAK,CAAC,WAAW,MAAM,CAAC,EACxB,SAAS,qDAAqD;AAAA,EACjE,QAAQ,EACL,OAAO;AAAA,IACN,SAAS,EACN,OAAO,EACP,MAAM,YAAY,EAClB,SAAS,0DAA0D;AAAA,IACtE,KAAK,EACF,OAAO;AAAA,MACN,MAAM,YAAY,aAAa;AAAA,MAC/B,aAAa,YAAY,WAAW;AAAA,MACpC,MAAM,YAAY,UAAU;AAAA,IAC9B,CAAC,EACA,OAAO,EACP;AAAA,MACC;AAAA,IACF;AAAA,EACJ,CAAC,EACA,OAAO,EACP,SAAS,2CAA2C,EACpD,SAAS;AAAA,EACZ,UAAU,EACP,KAAK,CAAC,QAAQ,SAAS,CAAC,EACxB;AAAA,IACC;AAAA,EACF;AACJ,CAAC,EACA,OAAO;AAIV,IAAM,UAAU,EACb,OAAO;AAAA,EACN,iBAAiB,EACd,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAI,EACR,SAAS,6CAA6C,EACtD,SAAS;AAAA,EACZ,aAAa,EACV,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EACvB,IAAI,GAAG,qCAAqC,EAC5C,IAAI,GAAG,qCAAqC,EAC5C;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,SAAS,SACN,SAAS,qDAAqD,EAC9D,SAAS;AAAA,EACZ,YAAY,SACT,SAAS,wCAAwC,EACjD,SAAS;AAAA,EACZ,YAAY,SACT;AAAA,IACC;AAAA,EACF,EACC,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,uBAAuB,EAC1B,OAAO;AAAA,EACN,OAAO,WAAW;AAAA,IAChB;AAAA,EACF;AAAA,EACA,QAAQ,WAAW;AAAA,IACjB;AAAA,EACF;AAAA,EACA,OAAO,EACJ,KAAK,CAAC,MAAM,MAAM,IAAI,CAAC,EACvB;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,MAAM,EACH,KAAK,CAAC,QAAQ,OAAO,CAAC,EACtB,SAAS,8DAA8D,EACvE,SAAS;AACd,CAAC,EACA,OAAO;AAEV,IAAM,iBAAiB,EACpB,OAAO;AAAA,EACN,SAAS,EACN,OAAO,EACP;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,IAAI,EACD,OAAO,EACP,MAAM,IAAI,EACV;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN;AAAA,IACC;AAAA,EACF;AAAA,EACF,aAAa,EACV,OAAO,EACP,IAAI,EAAE,EACN,IAAI,GAAG,EACP;AAAA,IACC;AAAA,EACF;AAAA,EACF,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAG,EACP;AAAA,IACC;AAAA,EACF;AAAA,EACF,UAAU,EACP,KAAK,YAAY;AAAA,IAChB,OACE;AAAA,EACJ,CAAC,EACA,SAAS,oDAAoD;AAAA,EAChE,KAAK,EACF,OAAO,EACP,IAAI,CAAC,EACL,IAAI,EAAE,EACN;AAAA,IACC;AAAA,EACF;AAAA,EACF,YAAY,EACT,KAAK,CAAC,UAAU,YAAY,SAAS,CAAC,EACtC;AAAA,IACC;AAAA,EACF;AAAA,EACF,cAAc,EACX,KAAK,CAAC,WAAW,cAAc,CAAC,EAChC;AAAA,IACC;AAAA,EACF;AAAA,EACF,QAAQ,EACL,MAAM,UAAU,EAChB,IAAI,CAAC,EACL,IAAI,WAAW,MAAM,EACrB;AAAA,IACC;AAAA,EACF;AAAA,EACF,aAAa,EACV,OAAO;AAAA,IACN,OAAO,EACJ,MAAM,gBAAgB,EACtB,IAAI,EAAE,EACN;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,MAAM,EACH,MAAM,eAAe,EACrB,IAAI,EAAE,EACN,SAAS,kDAAkD,EAC3D,SAAS;AAAA,IACZ,SAAS,EACN,MAAM,kBAAkB,EACxB,IAAI,EAAE,EACN,SAAS,+CAA+C,EACxD,SAAS;AAAA,IACZ,QAAQ,EACL,MAAM,kBAAkB,EACxB,IAAI,EAAE,EACN;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,SAAS,EACN,MAAM,kBAAkB,EACxB,IAAI,EAAE,EACN;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,YAAY,EACT,MAAM,qBAAqB,EAC3B,IAAI,EAAE,EACN,SAAS,2CAA2C,EACpD,SAAS;AAAA,IACZ,SAAS,EACN,MAAM,kBAAkB,EACxB,IAAI,EAAE,EACN;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,UAAU,qBACP,SAAS,kDAAkD,EAC3D,SAAS;AAAA,IACZ,SAAS,EACN,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,GAAG,iBAAiB,EACtD;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,WAAW,sBACR;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,UAAU,EACP,MAAM,mBAAmB,EACzB,IAAI,EAAE,EACN;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,IAAI,eACD;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,MAAM,iBACH;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,EACd,CAAC,EACA,OAAO,EACP;AAAA,IACC;AAAA,EACF;AAAA,EACF,QAAQ,EACL,OAAO;AAAA,IACN,SAAS,SAAS;AAAA,MAChB;AAAA,IACF;AAAA,IACA,gBAAgB,SACb;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,YAAY,EACT;AAAA,MACC,EACG,OAAO,EACP;AAAA,QACC;AAAA,QACA;AAAA,MACF;AAAA,MACF;AAAA,IACF,EACC;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,IACZ,QAAQ,EACL;AAAA,MACC,EAAE,KAAK,QAAQ;AAAA,QACb,OAAO,WACL,IAAI,OAAO,MAAM,KAAK,CAAC;AAAA,MAC3B,CAAC;AAAA,IACH,EACC;AAAA,MACC;AAAA,IACF,EACC,SAAS;AAAA,EACd,CAAC,EACA,OAAO,EACP;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,WAAW,EACR;AAAA,IACC,EACG,OAAO,EACP;AAAA,MACC;AAAA,MACA;AAAA,IACF,EACC,IAAI,EAAE;AAAA,EACX,EACC,IAAI,EAAE,EACN;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,SAAS,QACN;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,SAAS,EACN,MAAM,EACN;AAAA,IACC;AAAA,EACF,EACC,SAAS;AAAA,EACZ,OAAO,EACJ,MAAM,EACN;AAAA,IACC;AAAA,EACF,EACC,SAAS;AACd,CAAC,EACA,OAAO;AAEH,SAAS,UAAU,OAAyB;AACjD,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,eAAe,MAAM,MAAM,IAAI,GAAG;AAC3C,eAAW,SAAS,YAAY,KAAK,EAAE,MAAM,KAAK,GAAG;AACnD,UAAI,MAAM,WAAW,GAAG,EAAG;AAC3B,YAAM,QAAQ,4BAA4B,KAAK,KAAK;AACpD,UAAI,MAAO,QAAO,IAAI,OAAO,MAAM,CAAC,CAAC,CAAC;AAAA,IACxC;AAAA,EACF;AACA,SAAO,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,MAAM,UAAU,OAAO,KAAK;AACvD;AAEA,SAAS,QAAQ,aAAuD;AACtE,UACG,YAAY,OAAO,UAAU,MAC7B,YAAY,MAAM,UAAU,MAC5B,YAAY,SAAS,UAAU,MAC/B,YAAY,QAAQ,UAAU,MAC9B,YAAY,SAAS,UAAU,MAC/B,YAAY,YAAY,UAAU,MAClC,YAAY,SAAS,UAAU,MAC/B,YAAY,WAAW,IAAI;AAEhC;AAEA,SAAS,mBACP,aACS;AACT,UACG,YAAY,OAAO,UAAU,KAAK,MAClC,YAAY,MAAM,UAAU,KAAK,MACjC,YAAY,SAAS,UAAU,KAAK,MACpC,YAAY,YAAY,UAAU,KAAK,MACvC,YAAY,SAAS,UAAU,KAAK,KACrC,YAAY,aAAa;AAE7B;AAEO,IAAM,iBAAiB,eAAe,YAAY,CAAC,UAAU,QAAQ;AAC1E,QAAM,UAAU,IAAI,IAAY,SAAS,OAAO,IAAI,WAAS,MAAM,GAAG,CAAC;AACvE,QAAM,cAAc,SAAS;AAE7B,QAAM,eAAe,CACnB,KACA,MACA,YACS;AACT,QAAI,QAAQ,IAAI,GAAG,EAAG;AACtB,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN;AAAA,MACA,SAAS,WAAW,GAAG,WAAW,OAAO;AAAA,IAC3C,CAAC;AAAA,EACH;AAEA,aAAW,CAAC,OAAO,MAAM,MAAM,YAAY,WAAW,CAAC,GAAG,QAAQ,GAAG;AACnE,UAAM,QAAQ,aAAa,OAAO,IAAI;AACtC,QAAI,CAAC,MAAM,SAAS,OAAO,IAAI,GAAG;AAChC,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,eAAe,WAAW,OAAO,MAAM;AAAA,QAC9C,SAAS,SAAS,OAAO,IAAI,mBAAmB,OAAO,IAAI,uBAAuB,MAAM,KAAK,IAAI,CAAC;AAAA,MACpG,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,mBAAmB,WAAW,GAAG;AACnC;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,IAAI,IAAI;AAC7B,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,MAAM,CAAC,aAAa;AAAA,MACpB,SACE;AAAA,IACJ,CAAC;AAAA,EACH;AAEA,MAAI,YAAY,SAAS;AACvB;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AACA,QAAI,OAAO,KAAK,YAAY,OAAO,EAAE,SAAS,IAAI;AAChD,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,eAAe,SAAS;AAAA,QAC/B,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AACA,QAAI,cAAc,YAAY,SAAS;AACrC,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,eAAe,WAAW,UAAU;AAAA,QAC3C,SACE;AAAA,MACJ,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,YAAY,WAAW,SAAS,QAAQ;AAC1C;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,QAAM,kBAAkB,CAAC,OACvB,IAAI;AAAA,KACD,YAAY,YAAY,CAAC,GACvB,OAAO,aAAW,QAAQ,OAAO,EAAE,EACnC,IAAI,aAAW,QAAQ,KAAK;AAAA,EACjC;AAEF,QAAM,eAAe,gBAAgB,QAAQ;AAC7C,QAAM,eAAe,gBAAgB,OAAO;AAE5C,OACG,YAAY,YAAY,CAAC,GAAG;AAAA,IAC3B,aAAW,QAAQ,OAAO,WAAW,QAAQ,OAAO;AAAA,EACtD,GACA;AACA;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,aAAW,CAAC,OAAO,MAAM,MAAM,YAAY,WAAW,CAAC,GAAG,QAAQ,GAAG;AACnE,QAAI,CAAC,aAAa,IAAI,OAAO,KAAK,GAAG;AACnC,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,eAAe,WAAW,OAAO,OAAO;AAAA,QAC/C,SAAS,UAAU,OAAO,KAAK;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,aAAW,CAAC,OAAO,MAAM,MAAM,YAAY,WAAW,CAAC,GAAG,QAAQ,GAAG;AACnE,QAAI,CAAC,aAAa,IAAI,OAAO,KAAK,GAAG;AACnC,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,eAAe,WAAW,OAAO,OAAO;AAAA,QAC/C,SAAS,UAAU,OAAO,KAAK;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,OAAK,YAAY,SAAS,UAAU,KAAK,GAAG;AAC1C,iBAAa,aAAa,CAAC,QAAQ,GAAG,+BAA+B;AACrE;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,IAAI;AAAA,KACpB,YAAY,WAAW,WAAW,CAAC,GAAG,IAAI,YAAU,OAAO,GAAG;AAAA,EACjE;AAEA,aAAW,CAAC,OAAO,MAAM,MAAM,YAAY,UAAU,CAAC,GAAG,QAAQ,GAAG;AAClE,QAAI,CAAC,WAAW,IAAI,OAAO,MAAM,GAAG;AAClC,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,eAAe,UAAU,OAAO,QAAQ;AAAA,QAC/C,SAAS,WAAW,OAAO,MAAM;AAAA,MACnC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,KAAK,YAAY;AACvB,MAAI,IAAI,OAAO,UAAU,IAAI,UAAU,QAAQ;AAC7C,iBAAa,YAAY,CAAC,QAAQ,GAAG,qCAAqC;AAAA,EAC5E;AACA,MAAI,IAAI,SAAS,QAAQ;AACvB;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AACA,eAAW,CAAC,OAAO,MAAM,KAAK,GAAG,QAAQ,QAAQ,GAAG;AAClD,UAAI,CAAC,QAAQ,IAAI,OAAO,KAAK,GAAG;AAC9B,YAAI,SAAS;AAAA,UACX,MAAM;AAAA,UACN,MAAM,CAAC,eAAe,MAAM,WAAW,OAAO,OAAO;AAAA,UACrD,SAAS,WAAW,OAAO,KAAK,uBAAuB,OAAO,EAAE;AAAA,QAClE,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,UAAU,IAAI,SAAS,UAAU,IAAI,SAAS,QAAQ;AACpE;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,KAAK,IAAI,SAAS,CAAC,GAAG,IAAI,UAAQ,KAAK,EAAE,CAAC;AAC9D,aAAW,CAAC,OAAO,MAAM,MAAM,IAAI,WAAW,CAAC,GAAG,QAAQ,GAAG;AAC3D,eAAW,CAAC,YAAY,MAAM,MAAM,OAAO,SAAS,CAAC,GAAG,QAAQ,GAAG;AACjE,UAAI,CAAC,QAAQ,IAAI,MAAM,GAAG;AACxB,YAAI,SAAS;AAAA,UACX,MAAM;AAAA,UACN,MAAM,CAAC,eAAe,MAAM,WAAW,OAAO,SAAS,UAAU;AAAA,UACjE,SAAS,SAAS,MAAM;AAAA,QAC1B,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,YAAY,MAAM;AACpB;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AACA;AAAA,MACE;AAAA,MACA,CAAC,QAAQ;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cACJ,YAAY,cAAc,WACzB,YAAY,UAAU,UAAU,KAAK,KACtC,OAAO,UACP,YAAY,SAAS,UACrB,SAAS,QAAQ,eAAe;AAElC,MAAI,eAAe,CAAC,SAAS,QAAQ,SAAS;AAC5C,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,MAAM,CAAC,UAAU,SAAS;AAAA,MAC1B,SACE;AAAA,IACJ,CAAC;AAAA,EACH;AAEA,QAAM,aAAa,SAAS,QAAQ,cAAc,CAAC;AACnD,MAAI,OAAO,KAAK,UAAU,EAAE,SAAS,IAAI;AACvC,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,MAAM,CAAC,UAAU,YAAY;AAAA,MAC7B,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,aAAW,SAAS,SAAS,QAAQ;AACnC,UAAM,aAAa,UAAU,MAAM,GAAG;AACtC,QAAI,YAAY,yBAAyB,CAAC,MAAM,eAAe;AAC7D,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,QAAQ;AAAA,QACf,SAAS,GAAG,MAAM,GAAG;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,SAAS,eAAe,UAAU;AACpC,UAAM,UAAU,eAAe;AAAA,MAC7B,WAAS,SAAS,UAAU,KAAK,MAAM;AAAA,IACzC;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,SAAS;AAAA,QAChB,SAAS,oDAAoD,QAAQ,KAAK,IAAI,CAAC;AAAA,MACjF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,SAAS,iBAAiB,gBAAgB;AAC5C,UAAM,YAAa,CAAC,SAAS,QAAQ,WAAW,QAAQ,EAAY;AAAA,MAClE,UAAQ,YAAY,GAAG,GAAG,UAAU,KAAK;AAAA,IAC3C;AACA,QAAI,UAAU,SAAS,GAAG;AACxB,UAAI,SAAS;AAAA,QACX,MAAM;AAAA,QACN,MAAM,CAAC,cAAc;AAAA,QACrB,SAAS,8HAA8H,UAAU,KAAK,IAAI,CAAC;AAAA,MAC7J,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MACE,CAAC,UAAU,SAAS,GAAG,EAAE,KAAK,WAAS,kBAAkB,SAAS,KAAK,CAAC,GACxE;AACA,QAAI,SAAS;AAAA,MACX,MAAM;AAAA,MACN,MAAM,CAAC,KAAK;AAAA,MACZ,SAAS,6BAA6B,kBAAkB,KAAK,IAAI,CAAC;AAAA,IACpE,CAAC;AAAA,EACH;AACF,CAAC;AAEM,SAAS,qBAA8C;AAC5D,QAAM,EAAE,SAAS,GAAG,UAAU,IAAI,EAAE,aAAa,gBAAgB;AAAA,IAC/D,QAAQ;AAAA,EACV,CAAC;AACD,SAAO;AAAA,IACL;AAAA,IACA,KAAK;AAAA,IACL,OAAO;AAAA,IACP,aACE;AAAA,IACF,GAAG;AAAA,EACL;AACF;AAMA,SAAS,aAAa,OAAiC;AACrD,MAAI,MAAM,SAAS,cAAe,QAAO,MAAM;AAC/C,SAAO,MAAM,OAAO,CAAC,GAAG,WAAW,MAAM;AAC3C;AAEO,SAAS,iBAAiB,OAAoC;AACnE,QAAM,SAAS,eAAe,UAAU,KAAK;AAC7C,MAAI,OAAO,SAAS;AAClB,WAAO,EAAE,IAAI,MAAM,UAAU,OAAO,KAAK;AAAA,EAC3C;AAEA,QAAM,SAA0B,OAAO,MAAM,OAAO,IAAI,YAAU;AAAA,IAChE,MAAM,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,GAAG,IAAI;AAAA,IACrD,SAAS,aAAa,KAAK;AAAA,EAC7B,EAAE;AAEF,SAAO,EAAE,IAAI,OAAO,OAAO;AAC7B;","names":[]}
1
+ {"version":3,"sources":["../src/manifest/schema.ts"],"sourcesContent":["import { z } from 'zod';\nimport {\n CATEGORIES,\n CHARTS,\n EVENTS,\n PAGE_KEYS,\n SCOPE_KEYS,\n SERVED_SDK_MAJORS,\n SLOTS,\n TABLE_KEYS,\n findScope,\n slotsForPage,\n} from './constants';\nimport type {\n ExtensionManifest,\n ManifestIssue,\n ManifestValidation,\n WorkDirection,\n} from './types';\n\nconst SLUG = /^[a-z][a-z0-9-]{2,39}$/;\nconst CONTRIBUTION_ID = /^[a-z][a-z0-9-]{1,39}$/;\nconst SNAKE_CASE = /^[a-z][a-z0-9_]*$/;\nconst PROCEDURE_NAME = /^[a-zA-Z][a-zA-Z0-9_-]{1,59}$/;\nconst DOMAIN_EVENT = /^[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*$/;\n\nconst jsonSchema = z\n .record(z.string(), z.unknown())\n .describe(\n 'A JSON Schema object. `tappify extension types` turns it into a TypeScript type.',\n );\n\n/** The serialised size Tappify accepts for a tool or action input schema. */\nexport const AI_INPUT_SCHEMA_MAX_BYTES = 16_384;\n\n/** The length Tappify accepts for one `title` or `description` inside it. */\nexport const AI_SCHEMA_TEXT_MAX = 500;\n\nconst AI_SCHEMA_TEXT_KEYS = new Set(['title', 'description']);\nconst AI_SCHEMA_MAX_DEPTH = 12;\n\nfunction overlongSchemaText(value: unknown, depth = 0): string | null {\n if (depth > AI_SCHEMA_MAX_DEPTH) return null;\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n const found = overlongSchemaText(entry, depth + 1);\n if (found !== null) return found;\n }\n return null;\n }\n\n if (typeof value !== 'object' || value === null) return null;\n\n for (const [key, entry] of Object.entries(value)) {\n if (\n AI_SCHEMA_TEXT_KEYS.has(key) &&\n typeof entry === 'string' &&\n entry.length > AI_SCHEMA_TEXT_MAX\n ) {\n return key;\n }\n const found = overlongSchemaText(entry, depth + 1);\n if (found !== null) return found;\n }\n\n return null;\n}\n\n/**\n * The assistant reads the `title` and `description` strings inside a tool or\n * action input schema the same way it reads the tool description, so Tappify\n * bounds them the same way. A schema past either bound is refused at publish,\n * and the CLI refuses it here rather than letting you find out on upload.\n */\nconst aiInputSchema = jsonSchema.superRefine((value, ctx) => {\n const size = new TextEncoder().encode(JSON.stringify(value)).length;\n if (size > AI_INPUT_SCHEMA_MAX_BYTES) {\n ctx.addIssue({\n code: 'custom',\n message: `This input schema serialises to ${size} bytes; Tappify accepts at most ${AI_INPUT_SCHEMA_MAX_BYTES}. Describe the arguments in fewer words, or take fewer of them.`,\n });\n }\n\n const overlong = overlongSchemaText(value);\n if (overlong !== null) {\n ctx.addIssue({\n code: 'custom',\n message: `A \"${overlong}\" inside this input schema is longer than ${AI_SCHEMA_TEXT_MAX} characters. The assistant reads it like the tool description, which is bounded the same way.`,\n });\n }\n});\n\nconst httpsUrl = z.url().startsWith('https://');\nconst entryPath = z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The source file that renders this contribution, as a path relative to the manifest. It has to be present as an expose in the built bundle.',\n );\nconst contributionId = z\n .string()\n .regex(CONTRIBUTION_ID)\n .describe(\n 'The id for this contribution, and the handle the host and your generated types refer to it by.',\n );\nconst title = z\n .string()\n .min(1)\n .max(60)\n .describe('The label owners read on this contribution.');\n\nconst scopeEntry = z\n .object({\n key: z\n .enum(SCOPE_KEYS, {\n error: 'Unknown scope key; see the Scopes reference.',\n })\n .describe('The scope key, from the scopes reference.'),\n justification: z\n .string()\n .min(20)\n .max(500)\n .describe(\n 'Why this extension asks for the scope, in your own words. Shown on the consent screen, and required for the scopes that ask for one.',\n )\n .optional(),\n })\n .strict();\n\nconst mobile = z\n .boolean()\n .describe(\n 'Whether this surface is drawn on phone layouts. Defaults to true; set false to hide it there.',\n )\n .optional();\n\nconst hostPage = (what: string) => z.enum(PAGE_KEYS).describe(what);\n\nconst pageContribution = z\n .object({\n id: contributionId,\n title,\n entry: entryPath,\n nav: z\n .boolean()\n .describe(\n 'Whether this page also gets an entry in the project navigation. The route `/projects/:projectId/ext/:extensionId/:pageId` exists either way.',\n )\n .optional(),\n mobile,\n })\n .strict();\n\nconst tabContribution = z\n .object({\n id: contributionId,\n title,\n entry: entryPath,\n page: hostPage('The host page this tab sits on.'),\n mobile,\n })\n .strict();\n\nconst widgetContribution = z\n .object({\n id: contributionId,\n title,\n entry: entryPath,\n page: hostPage('The host page this widget renders on.'),\n slot: z\n .enum(SLOTS)\n .describe(\n 'The named anchor on that page. A slot that is not on the page fails validation.',\n ),\n size: z\n .enum(['1x1', '2x1', '2x2'])\n .describe(\n \"The widget's footprint in the slot grid, as columns by rows. Every slot takes all three.\",\n ),\n expandable: z\n .boolean()\n .describe(\n 'Whether owners can open the same component in the expand panel.',\n )\n .optional(),\n })\n .strict();\n\nconst seriesContribution = z\n .object({\n id: contributionId,\n label: title,\n chart: z.enum(CHARTS).describe('The host chart this line is drawn on.'),\n metric: z\n .string()\n .min(1)\n .max(60)\n .describe(\n 'The connector metric this line plots. It has to be one the connector declares.',\n ),\n })\n .strict();\n\nconst bannerContribution = z\n .object({\n id: contributionId,\n page: hostPage('The host page this banner appears on.'),\n event: z\n .string()\n .regex(DOMAIN_EVENT)\n .describe(\n 'The event that raises it. Declare the same name under `contributes.webhooks` with `as: \"banner\"`.',\n ),\n })\n .strict();\n\nconst rowActionContribution = z\n .object({\n id: contributionId,\n title,\n table: z\n .enum(TABLE_KEYS)\n .describe('The host table whose row menu gains this entry.'),\n entry: entryPath,\n })\n .strict();\n\nconst markerContribution = z\n .object({\n id: contributionId,\n label: title,\n chart: z\n .union([z.enum(CHARTS), z.literal('*')])\n .describe(\n 'The host chart this marker is drawn on, or `*` for every chart.',\n ),\n event: z\n .string()\n .regex(DOMAIN_EVENT)\n .describe(\n 'The event that places it on the time axis. Declare the same name under `contributes.webhooks` with `as: \"event\"`.',\n ),\n glyph: z\n .string()\n .min(1)\n .max(2)\n .describe('The one or two characters drawn at the marker.'),\n })\n .strict();\n\nconst settingsContribution = z\n .object({\n entry: entryPath,\n schema: jsonSchema.describe(\n 'The JSON Schema for the values owners fill in. It becomes `TapSettingsProps[\"values\"]` and the reserved `settings` singleton.',\n ),\n })\n .strict();\n\nconst storageCollection = z\n .object({\n scope: z\n .enum(['user', 'install', 'organization'])\n .describe(\n 'Who the documents belong to: one `user`, the whole `install`, or the `organization`.',\n ),\n singleton: z\n .boolean()\n .describe(\n 'Whether the collection holds exactly one document rather than many.',\n )\n .optional(),\n schema: jsonSchema.describe(\n 'The JSON Schema for one document in this collection.',\n ),\n })\n .strict();\n\nconst credentialField = z\n .object({\n name: z\n .string()\n .regex(SNAKE_CASE)\n .describe('The key your server reads this value back as.'),\n label: z\n .string()\n .min(1)\n .max(60)\n .describe('The label owners read above the input.'),\n type: z\n .enum(['text', 'password', 'textarea', 'file', 'select'])\n .describe('How the input is drawn to the owner.'),\n required: z\n .boolean()\n .describe('Whether owners have to fill it in before the install works.'),\n options: z\n .array(z.string())\n .describe('The choices offered, for a `select` field.')\n .optional(),\n help: z\n .string()\n .max(200)\n .describe('The hint drawn under the input.')\n .optional(),\n })\n .strict();\n\nconst connectorContribution = z\n .object({\n auth: z\n .object({\n method: z\n .enum(['none', 'api_key', 'oauth'])\n .describe('The credential flow owners go through at install.'),\n fields: z\n .array(credentialField)\n .max(20)\n .describe(\n 'The credentials owners enter. They reach your server as `credentials` on every call.',\n )\n .optional(),\n oauth: z\n .object({\n authorizationUrl: httpsUrl.describe(\n 'Where owners are sent to approve access.',\n ),\n tokenUrl: httpsUrl.describe(\n 'Where the authorization code is exchanged for a token.',\n ),\n scopes: z\n .array(z.string())\n .describe(\n \"The scopes asked of the outside service. These are that service's scopes, not Tappify's.\",\n ),\n })\n .strict()\n .describe('The endpoints, for `method: \"oauth\"`.')\n .optional(),\n })\n .strict()\n .describe('How owners authenticate this connector.')\n .optional(),\n sync: z\n .enum(['5m', '15m', '1h', '6h', '24h'])\n .describe('How often Tappify asks your server to sync.')\n .optional(),\n metrics: z\n .array(\n z\n .object({\n key: z\n .string()\n .regex(SNAKE_CASE)\n .describe('The metric key. A series names it to draw its line.'),\n label: z\n .string()\n .min(1)\n .max(60)\n .describe('The label owners read on the metric.'),\n unit: z\n .enum(['count', 'ratio', 'currency', 'seconds', 'bytes'])\n .describe('How the value is formatted.'),\n kind: z\n .enum(['gauge', 'counter'])\n .describe(\n 'Whether the value is a level at a point in time (`gauge`) or accumulates (`counter`).',\n ),\n dimensions: z\n .array(\n z\n .object({\n key: z\n .string()\n .regex(SNAKE_CASE)\n .describe('The dimension key.'),\n label: z\n .string()\n .min(1)\n .max(60)\n .describe('The label owners read on the dimension.'),\n })\n .strict(),\n )\n .max(10)\n .describe('The breakdowns this metric can be split by.')\n .optional(),\n backfillDays: z\n .number()\n .int()\n .min(1)\n .max(90)\n .describe('How many days of history to fetch on the first sync.')\n .optional(),\n })\n .strict(),\n )\n .max(50)\n .describe(\n 'The metrics this connector writes into the metrics store. Needs the `metrics:write` scope.',\n )\n .optional(),\n })\n .strict();\n\nconst webhookContribution = z\n .object({\n event: z\n .string()\n .regex(DOMAIN_EVENT)\n .describe('The event name your server posts, as `domain.thing`.'),\n as: z\n .enum(['alert', 'banner', 'event'])\n .describe(\n 'What Tappify turns it into: an inbox `alert`, a page `banner`, or an `event` a chart marker and `tap.data.subscribe` can read.',\n ),\n payload: jsonSchema.describe(\n 'The JSON Schema for the body your server posts. An alert or a banner has to carry `why`.',\n ),\n })\n .strict();\n\nconst aiContribution = z\n .object({\n tools: z\n .array(\n z\n .object({\n id: contributionId,\n description: z\n .string()\n .min(10)\n .max(500)\n .describe(\n 'What this tool does, in the words the assistant reads when it decides whether to call it.',\n ),\n input: aiInputSchema.describe(\n 'The JSON Schema for the arguments the assistant passes. The assistant reads the `title` and `description` strings inside it, so they are scanned at publish and the whole schema is bounded at 16 KB.',\n ),\n returns: z\n .enum(['comparison', 'table', 'series', 'list', 'value'])\n .describe('The card the host draws from the result.'),\n cost: z\n .enum(['low', 'medium', 'high'])\n .describe(\n 'Roughly what one call costs to serve, so the assistant can prefer a cheaper tool.',\n ),\n cache: z\n .enum(['1m', '5m', '1h'])\n .describe(\n 'How long an identical call may be served from cache instead of reaching your server.',\n )\n .optional(),\n })\n .strict(),\n )\n .max(15, 'An extension declares at most 15 assistant tools.')\n .describe(\n 'The tools the assistant may call on your server. Needs the `ai:tools` scope.',\n )\n .optional(),\n mentions: z\n .array(\n z\n .object({\n id: contributionId,\n label: title,\n list: z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The path that answers the picker with `{ items: [{ id, label }] }`. The host always calls `/tappify/mentions/<id>` under `server.baseUrl` — the route `createTappifyHandler` serves — so write that path here; it records the route, it does not choose it.',\n ),\n })\n .strict(),\n )\n .max(10)\n .describe(\n 'The things owners can name with @ in chat, and where to list them. Needs the `ai:tools` scope.',\n )\n .optional(),\n actions: z\n .array(\n z\n .object({\n id: contributionId,\n description: z\n .string()\n .min(10)\n .max(500)\n .describe(\n 'What this action does, in the words the owner will read on the approval card.',\n ),\n input: aiInputSchema.describe(\n 'The JSON Schema for the arguments the assistant passes. The assistant reads the `title` and `description` strings inside it, so they are scanned at publish and the whole schema is bounded at 16 KB.',\n ),\n approval: z\n .literal(true)\n .describe(\n 'Always true. An action goes through an owner approval; there is no way to opt out.',\n ),\n reversible: z\n .boolean()\n .describe('Whether the owner can undo it after it runs.'),\n scope: z\n .enum(SCOPE_KEYS)\n .describe(\n 'The scope this action needs. Declare it under `scopes` as well.',\n ),\n estimatesCost: z\n .boolean()\n .describe(\n 'Whether the approval card will show what the action costs.',\n )\n .optional(),\n })\n .strict(),\n )\n .max(10, 'An extension declares at most 10 assistant actions.')\n .describe(\n \"The actions the assistant may run, each behind an owner approval. Needs the `ai:actions` scope, and each action's own scope. `tap.actions.run` creates the run and resolves with it; the host draws the approval card and calls your server only once the owner approves.\",\n )\n .optional(),\n knowledge: z\n .array(\n z\n .object({\n id: contributionId,\n category: z\n .enum(['metric_definitions', 'gotchas', 'setup'])\n .describe('What kind of knowledge the file carries.'),\n file: z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The markdown file in the bundle, as a path relative to the manifest.',\n ),\n })\n .strict(),\n )\n .max(20)\n .describe(\n 'Markdown the assistant indexes and cites when it answers about this extension.',\n )\n .optional(),\n skills: z\n .array(\n z\n .object({\n id: contributionId,\n name: z\n .string()\n .min(1)\n .max(60)\n .describe('The skill name the assistant matches on.'),\n description: z\n .string()\n .min(10)\n .max(300)\n .describe('When the assistant should reach for this skill.'),\n file: z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The markdown file in the bundle, as a path relative to the manifest.',\n ),\n tools: z\n .array(z.string())\n .max(15)\n .describe('The tool ids this skill is allowed to use.')\n .optional(),\n })\n .strict(),\n )\n .max(5, 'An extension declares at most 5 skills.')\n .describe(\n 'Markdown procedures that shape how the assistant works. Needs the `ai:skills` scope.',\n )\n .optional(),\n prompts: z\n .array(\n z\n .object({\n id: contributionId,\n title,\n template: z\n .string()\n .min(1)\n .max(2000)\n .describe(\n 'The prompt text, with `{{project.*}}`, `{{context.*}}` and `{{input.*}}` placeholders. The owner sees it resolved before it is sent.',\n ),\n surfaces: z\n .array(\n z.enum(['chat_suggestion', 'widget', 'page', 'result_card']),\n )\n .min(1)\n .describe('Where the host draws this prompt as a chip.'),\n after: z\n .array(z.string())\n .max(15)\n .describe(\n \"The tool ids whose results this prompt follows up on. They have to be this extension's own.\",\n )\n .optional(),\n input: jsonSchema\n .describe(\n 'The JSON Schema for a short form the owner fills in before the prompt is sent.',\n )\n .optional(),\n })\n .strict(),\n )\n .max(20, 'An extension declares at most 20 prompt templates.')\n .describe(\n 'Prompt templates the host offers owners as chips. Needs the `ai:skills` scope.',\n )\n .optional(),\n context: z\n .array(\n z\n .object({\n id: contributionId,\n description: z\n .string()\n .min(10)\n .max(300)\n .describe('What context this provider adds to the turn.'),\n when: z\n .array(z.enum(['turn_start', 'mention']))\n .min(1)\n .describe('When the host calls your server for it.'),\n cache: z\n .enum(['1m', '5m', '1h'])\n .describe('How long the context may be reused before refetching.')\n .optional(),\n })\n .strict(),\n )\n .max(3, 'An extension declares at most 3 context providers.')\n .describe(\n 'Providers the host calls to put your data in front of the assistant. Needs the `ai:skills` scope.',\n )\n .optional(),\n })\n .strict();\n\nconst statusNames = (stage: string) =>\n z.array(z.string()).describe(`Your own status names that mean ${stage}.`);\n\nconst legacyWorkContribution = z\n .object({\n kind: z\n .enum(['tracker', 'docs', 'chat'])\n .describe('What kind of destination this is.'),\n container: z\n .object({\n label: z\n .string()\n .min(1)\n .max(60)\n .describe(\n 'What you call a container, such as \"Project\", \"Space\" or \"Channel\". Owners read it when they pick one.',\n ),\n list: z\n .string()\n .min(1)\n .describe(\n 'The path under `server.baseUrl` that lists the containers an owner can choose from.',\n ),\n })\n .strict()\n .describe('Where items land, and how owners pick one.'),\n creates: z\n .array(z.enum(['issue', 'page', 'message']))\n .min(1)\n .describe('What this destination can create.'),\n fields: z\n .object({\n priority: z\n .array(z.string())\n .describe('Your own priority names, highest first.')\n .optional(),\n labels: z.boolean().describe('Whether items carry labels.').optional(),\n assignee: z\n .boolean()\n .describe('Whether items carry an assignee.')\n .optional(),\n required: z\n .array(z.string())\n .describe(\n 'The fields an owner has to fill in before you can create.',\n ),\n })\n .strict()\n .describe('What a created item carries.'),\n attachments: z\n .array(z.enum(['image', 'link']))\n .describe('What can be attached to a created item.'),\n comments: z\n .enum(['two-way', 'none'])\n .describe('Whether comments sync back and forth or not at all.'),\n status: z\n .object({\n webhook: z\n .string()\n .regex(DOMAIN_EVENT)\n .describe('The event your server posts when an item changes status.'),\n map: z\n .object({\n open: statusNames('not started'),\n in_progress: statusNames('under way'),\n done: statusNames('finished'),\n })\n .strict()\n .describe(\n 'Your status names, grouped into the three stages Tappify draws.',\n ),\n })\n .strict()\n .describe('How status travels back from your system.')\n .optional(),\n identity: z\n .enum(['user', 'install'])\n .describe(\n 'Whether items are created as the owner who asked (`user`) or as the install itself.',\n ),\n })\n .strict();\n\nconst workDirection = z.enum([\n 'none',\n 'to_provider',\n 'from_provider',\n 'two-way',\n]);\n\nconst workAuth = z.discriminatedUnion('method', [\n z\n .object({\n method: z.literal('oauth'),\n provider: z.string().min(1).describe('The OAuth provider identifier.'),\n scopes: z\n .array(z.string())\n .describe('The scopes requested from the provider.'),\n })\n .strict(),\n z\n .object({\n method: z.literal('api_key'),\n fields: z.array(credentialField).max(20),\n })\n .strict(),\n]);\n\nconst currentWorkContribution = legacyWorkContribution.extend({\n container: legacyWorkContribution.shape.container\n .describe(\n 'Deprecated. Resolve containers dynamically with describe_container.',\n )\n .optional(),\n fields: legacyWorkContribution.shape.fields\n .describe('Deprecated. Resolve fields dynamically with describe_container.')\n .optional(),\n comments: z.union([\n z\n .enum(['two-way', 'none'])\n .describe('Deprecated. Declare a comments capability object.'),\n z\n .object({\n direction: workDirection,\n supportsEdits: z.literal(false),\n supportsDeletes: z.literal(false),\n })\n .strict(),\n ]),\n status: z\n .object({\n direction: workDirection,\n webhook: z.string().regex(DOMAIN_EVENT).optional(),\n map: z\n .object({\n open: statusNames('not started'),\n in_progress: statusNames('under way'),\n done: statusNames('finished'),\n })\n .strict()\n .describe(\n 'Deprecated. Resolve statuses dynamically with describe_container.',\n )\n .optional(),\n })\n .strict()\n .optional(),\n auth: workAuth.describe('How owners authenticate the work destination.'),\n});\n\nconst workContribution = z.union([\n currentWorkContribution,\n legacyWorkContribution.describe(\n 'Deprecated SDK v2 work declaration. Migrate to work capabilities and auth.',\n ),\n]);\n\n// Every field is optional so a vendor can fill the listing one value at a time;\n// the public-visibility refinement below requires all five before publishing.\nconst listing = z\n .object({\n longDescription: z\n .string()\n .min(1)\n .max(5000)\n .describe('The body of your listing page, in markdown.')\n .optional(),\n screenshots: z\n .array(z.string().min(1))\n .min(1, 'A listing shows 1 to 6 screenshots.')\n .max(6, 'A listing shows 1 to 6 screenshots.')\n .describe(\n 'Screenshot files carried in the bundle, as paths relative to the manifest. png at 1600 by 1000.',\n )\n .optional(),\n website: httpsUrl\n .describe('Where owners go to learn more about this extension.')\n .optional(),\n supportUrl: httpsUrl\n .describe('Where owners go when something breaks.')\n .optional(),\n privacyUrl: httpsUrl\n .describe(\n 'Your privacy policy, linked from the consent screen before anyone installs.',\n )\n .optional(),\n })\n .strict();\n\nconst LISTING_FIELDS = [\n 'longDescription',\n 'screenshots',\n 'website',\n 'supportUrl',\n 'privacyUrl',\n] as const;\n\nconst procedureDeclaration = z\n .object({\n input: jsonSchema.describe(\n 'The JSON Schema for the argument your handler receives. The host validates against it before calling.',\n ),\n output: jsonSchema.describe(\n 'The JSON Schema for what your handler returns.',\n ),\n cache: z\n .enum(['1m', '5m', '1h'])\n .describe(\n 'How long an identical call may be served from cache instead of reaching your server.',\n )\n .optional(),\n kind: z\n .enum(['read', 'write'])\n .describe('Whether this procedure only reads or also changes something.')\n .optional(),\n })\n .strict();\n\nconst manifestObject = z\n .object({\n $schema: z\n .string()\n .describe(\n 'The URL of this JSON Schema. Editors read it for completion and inline validation.',\n )\n .optional(),\n id: z\n .string()\n .regex(SLUG)\n .describe(\n 'The identifier for this extension, unique across the Tap Store and fixed after the first publish.',\n ),\n name: z\n .string()\n .min(2)\n .max(40)\n .describe(\n 'The name owners see in the Tap Store and on every surface this extension renders.',\n ),\n description: z\n .string()\n .min(10)\n .max(200)\n .describe(\n 'The one line owners read on the Tap Store card and on the install screen.',\n ),\n icon: z\n .string()\n .min(1)\n .max(200)\n .describe(\n 'The icon file carried in the bundle, as a path relative to the manifest. Square png or svg, at most 512 KB.',\n ),\n category: z\n .enum(CATEGORIES, {\n error:\n 'Choose a category from the Tap Store list (see the Categories reference).',\n })\n .describe('The Tap Store section owners browse and filter by.'),\n sdk: z\n .string()\n .min(1)\n .max(60)\n .describe(\n 'The SDK range this build targets, such as \"^2.0.0\". The host serves one major at a time and rejects a range that excludes it.',\n ),\n visibility: z\n .enum(['public', 'unlisted', 'private'])\n .describe(\n 'Who can find and install this extension. \"public\" lists it in the Tap Store and needs all five listing fields; \"unlisted\" installs by link; \"private\" installs only in your own workspace.',\n ),\n installScope: z\n .enum(['project', 'organization'])\n .describe(\n 'Whether one install belongs to a single project or to a whole organization. An organization install contributes no pages, tabs, widgets or series, because those render on a project.',\n ),\n scopes: z\n .array(scopeEntry)\n .min(1)\n .max(SCOPE_KEYS.length)\n .describe(\n 'The permissions an owner grants at install. Declare only what your contributions use.',\n ),\n contributes: z\n .object({\n pages: z\n .array(pageContribution)\n .max(20)\n .describe(\n \"Pages of this extension's own, each on its own route under the project. `nav: true` also puts one in the project navigation.\",\n )\n .optional(),\n tabs: z\n .array(tabContribution)\n .max(20)\n .describe(\"Tabs beside the host's own on a named host page.\")\n .optional(),\n widgets: z\n .array(widgetContribution)\n .max(20)\n .describe('Cards drawn into a named slot on a host page.')\n .optional(),\n series: z\n .array(seriesContribution)\n .max(20)\n .describe(\n 'Lines drawn onto a host chart from a metric the connector writes. The host draws the line on the chart this names and puts the vendor in the chart legend; a stale metric is drawn dashed with the time of its last sync.',\n )\n .optional(),\n banners: z\n .array(bannerContribution)\n .max(20)\n .describe(\n 'Banners raised on a host page by a webhook declared with as: \"banner\". The host draws one at the top of that page, carrying the `why` sentence from the delivery, until the owner dismisses it.',\n )\n .optional(),\n rowActions: z\n .array(rowActionContribution)\n .max(20)\n .describe('Actions offered on a row of a host table.')\n .optional(),\n markers: z\n .array(markerContribution)\n .max(20)\n .describe(\n 'Markers drawn on a host chart by a webhook declared with as: \"event\". Needs the `ui:render` and `alerts:write` scopes. The host draws the glyph on the chart time axis at the moment the delivery arrived, with the vendor named on hover.',\n )\n .optional(),\n settings: settingsContribution\n .describe('The settings panel owners open for this install.')\n .optional(),\n storage: z\n .record(z.string().regex(SNAKE_CASE), storageCollection)\n .describe(\n 'The hosted storage collections this extension reads and writes. At most 10, and the name \"settings\" is reserved. Needs the `storage:write` scope.',\n )\n .optional(),\n connector: connectorContribution\n .describe(\n 'How this extension authenticates against an outside service, how often it syncs, and the metrics it writes.',\n )\n .optional(),\n webhooks: z\n .array(webhookContribution)\n .max(20)\n .describe(\n \"The events this extension's server posts to Tappify, and what each one becomes. An `alert` or a `banner` needs the `alerts:write` scope.\",\n )\n .optional(),\n ai: aiContribution\n .describe(\n 'What this extension offers the assistant: tools, mentions, actions, knowledge, skills, prompts and context providers.',\n )\n .optional(),\n work: workContribution\n .describe(\n 'The work destination this extension creates issues, pages or messages in. Needs `review.threads:read` and `work:create`; active comment or status sync also needs `work:sync`.',\n )\n .optional(),\n })\n .strict()\n .describe(\n 'What this extension adds to the host. At most 20 UI contributions in total, and a page, tab, widget, row action, marker or settings panel needs the `ui:render` scope.',\n ),\n server: z\n .object({\n baseUrl: httpsUrl.describe(\n 'Where the host calls your server. `<baseUrl>/tappify/health` answers 200 within three seconds at publish.',\n ),\n sandboxBaseUrl: httpsUrl\n .describe(\n 'Where dev installs and sandbox projects call instead. Defaults to `baseUrl`.',\n )\n .optional(),\n procedures: z\n .record(\n z\n .string()\n .regex(\n PROCEDURE_NAME,\n 'Procedure names are identifiers such as \"getSummary\" or \"get-summary\": letters, digits, _ and -, starting with a letter, at most 60 characters. They become URL segments and generated type names.',\n ),\n procedureDeclaration,\n )\n .describe(\n 'The procedures the host may call on your server, by name, each with its input and output schema. At most 30.',\n )\n .optional(),\n events: z\n .array(\n z.enum(EVENTS, {\n error: issue =>\n `\"${String(issue.input)}\" is not a Tappify event. Subscribe to a name from the event catalogue.`,\n }),\n )\n .describe(\n 'The Tappify events your server subscribes to. Each is delivered to `POST /tappify/events` as it happens, with the same body every host-to-vendor call carries.',\n )\n .optional(),\n })\n .strict()\n .describe(\n 'Your own server: where the host calls it, and what it answers. Required once you declare a connector, a webhook, an assistant contribution, a work destination or a procedure.',\n )\n .optional(),\n telemetry: z\n .array(\n z\n .string()\n .regex(\n SNAKE_CASE,\n 'Telemetry names are snake_case, such as \"funnel_viewed\".',\n )\n .max(60),\n )\n .max(20)\n .describe(\n 'The telemetry names this extension may report. `tap.telemetry.event` rejects a name that is not declared here.',\n )\n .optional(),\n listing: listing\n .describe(\n 'What owners read on your Tap Store listing page. A public extension needs all five fields.',\n )\n .optional(),\n pricing: z\n .never()\n .describe(\n 'Reserved. Tappify sets pricing outside the manifest, so a manifest that carries this field fails validation.',\n )\n .optional(),\n score: z\n .never()\n .describe(\n 'Reserved. Tappify computes the quality score, so a manifest that carries this field fails validation.',\n )\n .optional(),\n })\n .strict();\n\nexport function sdkMajors(range: string): number[] {\n const majors = new Set<number>();\n for (const alternative of range.split('||')) {\n for (const token of alternative.trim().split(/\\s+/)) {\n if (token.startsWith('<')) continue;\n const match = /^(?:\\^|~|>=|>|=)?\\s*(\\d+)/.exec(token);\n if (match) majors.add(Number(match[1]));\n }\n }\n return [...majors].sort((left, right) => left - right);\n}\n\nfunction uiCount(contributes: ExtensionManifest['contributes']): number {\n return (\n (contributes.pages?.length ?? 0) +\n (contributes.tabs?.length ?? 0) +\n (contributes.widgets?.length ?? 0) +\n (contributes.series?.length ?? 0) +\n (contributes.banners?.length ?? 0) +\n (contributes.rowActions?.length ?? 0) +\n (contributes.markers?.length ?? 0) +\n (contributes.settings ? 1 : 0)\n );\n}\n\nfunction hasRenderedSurface(\n contributes: ExtensionManifest['contributes'],\n): boolean {\n return (\n (contributes.pages?.length ?? 0) > 0 ||\n (contributes.tabs?.length ?? 0) > 0 ||\n (contributes.widgets?.length ?? 0) > 0 ||\n (contributes.rowActions?.length ?? 0) > 0 ||\n (contributes.markers?.length ?? 0) > 0 ||\n contributes.settings !== undefined\n );\n}\n\nexport const manifestSchema = manifestObject.superRefine((manifest, ctx) => {\n const granted = new Set<string>(manifest.scopes.map(scope => scope.key));\n const contributes = manifest.contributes;\n\n const requireScope = (\n key: string,\n path: (string | number)[],\n because: string,\n ): void => {\n if (granted.has(key)) return;\n ctx.addIssue({\n code: 'custom',\n path,\n message: `Add the ${key} scope: ${because}`,\n });\n };\n\n for (const [index, widget] of (contributes.widgets ?? []).entries()) {\n const slots = slotsForPage(widget.page);\n if (!slots.includes(widget.slot)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'widgets', index, 'slot'],\n message: `Slot \"${widget.slot}\" is not on the ${widget.page} page. Pick one of: ${slots.join(', ')}.`,\n });\n }\n }\n\n if (hasRenderedSurface(contributes)) {\n requireScope(\n 'ui:render',\n ['scopes'],\n 'this extension renders UI in the host.',\n );\n }\n\n if (uiCount(contributes) > 20) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes'],\n message:\n 'An extension declares at most 20 UI contributions. Remove some, or split the extension in two.',\n });\n }\n\n if (contributes.storage) {\n requireScope(\n 'storage:write',\n ['scopes'],\n 'this extension declares hosted storage collections.',\n );\n if (Object.keys(contributes.storage).length > 10) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'storage'],\n message: 'An extension declares at most 10 storage collections.',\n });\n }\n if ('settings' in contributes.storage) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'storage', 'settings'],\n message:\n 'The collection name \"settings\" is reserved for the settings contribution. Rename this collection.',\n });\n }\n }\n\n if (contributes.connector?.metrics?.length) {\n requireScope(\n 'metrics:write',\n ['scopes'],\n 'the connector writes metrics into the metrics store.',\n );\n }\n\n const webhookEventsAs = (as: 'alert' | 'banner' | 'event'): Set<string> =>\n new Set(\n (contributes.webhooks ?? [])\n .filter(webhook => webhook.as === as)\n .map(webhook => webhook.event),\n );\n\n const bannerEvents = webhookEventsAs('banner');\n const markerEvents = webhookEventsAs('event');\n\n if (\n (contributes.webhooks ?? []).some(\n webhook => webhook.as === 'alert' || webhook.as === 'banner',\n )\n ) {\n requireScope(\n 'alerts:write',\n ['scopes'],\n 'a declared webhook becomes an alert or a banner.',\n );\n }\n\n for (const [index, banner] of (contributes.banners ?? []).entries()) {\n if (!bannerEvents.has(banner.event)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'banners', index, 'event'],\n message: `Event \"${banner.event}\" is not declared under webhooks. Declare it with as: \"banner\".`,\n });\n }\n }\n\n for (const [index, marker] of (contributes.markers ?? []).entries()) {\n if (!markerEvents.has(marker.event)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'markers', index, 'event'],\n message: `Event \"${marker.event}\" is not declared under webhooks. Declare it with as: \"event\".`,\n });\n }\n }\n\n if ((contributes.markers?.length ?? 0) > 0) {\n requireScope('ui:render', ['scopes'], 'markers draw on a host chart.');\n requireScope(\n 'alerts:write',\n ['scopes'],\n 'markers come from declared webhooks.',\n );\n }\n\n const metricKeys = new Set(\n (contributes.connector?.metrics ?? []).map(metric => metric.key),\n );\n\n for (const [index, series] of (contributes.series ?? []).entries()) {\n if (!metricKeys.has(series.metric)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'series', index, 'metric'],\n message: `Metric \"${series.metric}\" is not declared by the connector. Add it under contributes.connector.metrics.`,\n });\n }\n }\n\n const ai = contributes.ai;\n if (ai?.tools?.length || ai?.mentions?.length) {\n requireScope('ai:tools', ['scopes'], 'the assistant calls declared tools.');\n }\n if (ai?.actions?.length) {\n requireScope(\n 'ai:actions',\n ['scopes'],\n 'declared actions run behind approval.',\n );\n for (const [index, action] of ai.actions.entries()) {\n if (!granted.has(action.scope)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'ai', 'actions', index, 'scope'],\n message: `Add the ${action.scope} scope: the action \"${action.id}\" declares it.`,\n });\n }\n }\n }\n if (ai?.skills?.length || ai?.prompts?.length || ai?.context?.length) {\n requireScope(\n 'ai:skills',\n ['scopes'],\n 'skills, prompts and context providers shape the assistant.',\n );\n }\n\n const toolIds = new Set((ai?.tools ?? []).map(tool => tool.id));\n for (const [index, prompt] of (ai?.prompts ?? []).entries()) {\n for (const [afterIndex, toolId] of (prompt.after ?? []).entries()) {\n if (!toolIds.has(toolId)) {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'ai', 'prompts', index, 'after', afterIndex],\n message: `Tool \"${toolId}\" is not declared by this extension. Use one of your own tool ids.`,\n });\n }\n }\n }\n\n if (contributes.work) {\n const work = contributes.work;\n const legacy = isLegacyWork(work);\n if (!legacy || !granted.has('reviews:read')) {\n requireScope(\n 'review.threads:read',\n ['scopes'],\n 'the work destination reads Review threads.',\n );\n }\n requireScope(\n 'work:create',\n ['scopes'],\n 'the work destination creates items.',\n );\n const comments: WorkDirection =\n typeof work.comments === 'string'\n ? work.comments\n : work.comments.direction;\n const status: WorkDirection = work.status\n ? 'direction' in work.status\n ? work.status.direction\n : 'from_provider'\n : 'none';\n if (comments !== 'none' || status !== 'none') {\n requireScope(\n 'work:sync',\n ['scopes'],\n 'the work destination syncs comments or status.',\n );\n }\n if (work.identity === 'user') {\n ctx.addIssue({\n code: 'custom',\n path: ['contributes', 'work', 'identity'],\n message:\n 'Per-user work identity is not supported. Set identity to \"install\".',\n });\n }\n }\n\n const needsServer =\n contributes.connector !== undefined ||\n (contributes.webhooks?.length ?? 0) > 0 ||\n ai !== undefined ||\n contributes.work !== undefined ||\n manifest.server?.procedures !== undefined;\n\n if (needsServer && !manifest.server?.baseUrl) {\n ctx.addIssue({\n code: 'custom',\n path: ['server', 'baseUrl'],\n message:\n 'Set server.baseUrl: connectors, webhooks, assistant contributions, work destinations and procedures all call your server.',\n });\n }\n\n const procedures = manifest.server?.procedures ?? {};\n if (Object.keys(procedures).length > 30) {\n ctx.addIssue({\n code: 'custom',\n path: ['server', 'procedures'],\n message: 'An extension declares at most 30 procedures.',\n });\n }\n\n for (const scope of manifest.scopes) {\n const definition = findScope(scope.key);\n if (definition?.requiresJustification && !scope.justification) {\n ctx.addIssue({\n code: 'custom',\n path: ['scopes'],\n message: `${scope.key} needs a justification of 20 to 500 characters that tells the owner why you ask for it.`,\n });\n }\n }\n\n if (manifest.visibility === 'public') {\n const missing = LISTING_FIELDS.filter(\n field => manifest.listing?.[field] === undefined,\n );\n if (missing.length > 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['listing'],\n message: `A public extension needs a complete listing: add ${missing.join(', ')}. Set them with \\`tappify extension set listing.<field> <value>\\`, or keep visibility \"unlisted\" or \"private\" until the listing is ready.`,\n });\n }\n }\n\n if (manifest.installScope === 'organization') {\n const forbidden = (['pages', 'tabs', 'widgets', 'series'] as const).filter(\n key => (contributes[key]?.length ?? 0) > 0,\n );\n if (forbidden.length > 0) {\n ctx.addIssue({\n code: 'custom',\n path: ['installScope'],\n message: `Organization-scoped extensions cannot contribute widgets, tabs, pages or series, because those render on a project. Remove ${forbidden.join(', ')}, or set installScope to \"project\".`,\n });\n }\n }\n\n if (\n !sdkMajors(manifest.sdk).some(major => SERVED_SDK_MAJORS.includes(major))\n ) {\n ctx.addIssue({\n code: 'custom',\n path: ['sdk'],\n message: `The host serves SDK major ${SERVED_SDK_MAJORS.join(', ')}. Set sdk to a range that includes it, such as \"^2.0.0\".`,\n });\n }\n});\n\nexport function manifestJsonSchema(): Record<string, unknown> {\n const { $schema, ...generated } = z.toJSONSchema(manifestObject, {\n target: 'draft-2020-12',\n });\n return {\n $schema,\n $id: 'https://schema.tappify.ai/extension/v2.json',\n title: 'Tappify extension manifest',\n description:\n 'The tappify.extension.json file at the root of an extension repository.',\n ...generated,\n };\n}\n\n/**\n * A record whose key schema rejects reports only \"Invalid key in record\", so the\n * key schema's own message is lifted out of the nested issue.\n */\nfunction issueMessage(issue: z.core.$ZodIssue): string {\n if (issue.code !== 'invalid_key') return issue.message;\n return issue.issues[0]?.message ?? issue.message;\n}\n\nexport function validateManifest(value: unknown): ManifestValidation {\n const result = manifestSchema.safeParse(value);\n if (result.success) {\n return { ok: true, manifest: result.data };\n }\n\n const issues: ManifestIssue[] = result.error.issues.map(issue => ({\n path: issue.path.length > 0 ? issue.path.join('.') : '(root)',\n message: issueMessage(issue),\n }));\n\n return { ok: false, issues };\n}\n\nfunction isLegacyWork(\n work: NonNullable<ExtensionManifest['contributes']['work']>,\n): boolean {\n return !('auth' in work);\n}\n\n/** Migration warnings are separate from validation during SDK v2 compatibility. */\nexport function workManifestWarnings(\n manifest: ExtensionManifest,\n): ManifestIssue[] {\n const work = manifest.contributes.work;\n if (!work) return [];\n\n const warnings: ManifestIssue[] = [];\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- Migration diagnostics must inspect legacy fields.\n if (work.container) {\n warnings.push({\n path: 'contributes.work.container',\n message:\n 'Static work containers are deprecated. Resolve containers dynamically with describe_container.',\n });\n }\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- Migration diagnostics must inspect legacy fields.\n if (work.fields) {\n warnings.push({\n path: 'contributes.work.fields',\n message:\n 'Static work fields are deprecated. Resolve fields dynamically with describe_container.',\n });\n }\n if (typeof work.comments === 'string') {\n warnings.push({\n path: 'contributes.work.comments',\n message:\n 'String work comments are deprecated. Declare direction, supportsEdits: false and supportsDeletes: false.',\n });\n }\n if (work.status?.map) {\n warnings.push({\n path: 'contributes.work.status.map',\n message:\n 'Static work status maps are deprecated. Resolve statuses dynamically with describe_container.',\n });\n }\n if (\n isLegacyWork(work) &&\n manifest.scopes.some(scope => scope.key === 'reviews:read') &&\n !manifest.scopes.some(scope => scope.key === 'review.threads:read')\n ) {\n warnings.push({\n path: 'scopes',\n message:\n 'Using reviews:read for work is deprecated. Add review.threads:read for Review threads; reviews:read grants store-review access.',\n });\n }\n return warnings;\n}\n"],"mappings":"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","names":[]}
package/dist/server.cjs CHANGED
@@ -2,16 +2,20 @@
2
2
 
3
3
 
4
4
 
5
+ var _chunkEB5XWQAUcjs = require('./chunk-EB5XWQAU.cjs');
5
6
 
6
7
 
7
8
 
8
9
 
9
10
 
10
- var _chunkRMUWRESKcjs = require('./chunk-RMUWRESK.cjs');
11
- require('./chunk-PYJMJF44.cjs');
12
11
 
13
12
 
14
- var _chunk2DBSVKAUcjs = require('./chunk-2DBSVKAU.cjs');
13
+ var _chunkA74HCO6Tcjs = require('./chunk-A74HCO6T.cjs');
14
+ require('./chunk-7YKH4LZN.cjs');
15
+
16
+
17
+ var _chunk6HETNZJYcjs = require('./chunk-6HETNZJY.cjs');
18
+ require('./chunk-Q7SFCCGT.cjs');
15
19
 
16
20
  // src/server/adapters.ts
17
21
  function toHeaders(raw) {
@@ -58,8 +62,8 @@ async function expressBody(request) {
58
62
  return JSON.stringify(request.body);
59
63
  }
60
64
  if (!isReadable(request)) {
61
- throw new (0, _chunk2DBSVKAUcjs.TapServerError)(
62
- _chunkRMUWRESKcjs.SERVER_ERROR_CODES.BODY_INVALID,
65
+ throw new (0, _chunk6HETNZJYcjs.TapServerError)(
66
+ _chunkA74HCO6Tcjs.SERVER_ERROR_CODES.BODY_INVALID,
63
67
  "The Express request carried no parsed body and no readable stream, so the call would have run against an empty body. Mount express.json() before the Tappify middleware, or mount the middleware before any parser that consumes the stream.",
64
68
  400
65
69
  );
@@ -89,10 +93,10 @@ function toExpress(handler) {
89
93
  }
90
94
  await write(response, await handler(new Request(url, init)));
91
95
  })().catch((error) => {
92
- if (_chunk2DBSVKAUcjs.TapServerError.is(error)) {
96
+ if (_chunk6HETNZJYcjs.TapServerError.is(error)) {
93
97
  void write(
94
98
  response,
95
- _chunkRMUWRESKcjs.wireError.call(void 0, error.code, error.message, _nullishCoalesce(error.status, () => ( 400)))
99
+ _chunkA74HCO6Tcjs.wireError.call(void 0, error.code, error.message, _nullishCoalesce(error.status, () => ( 400)))
96
100
  );
97
101
  return;
98
102
  }
@@ -124,13 +128,13 @@ function toNode(handler) {
124
128
  response.writeHead(webResponse.status, headers);
125
129
  response.end(await webResponse.text());
126
130
  })().catch((error) => {
127
- const wire = _chunk2DBSVKAUcjs.TapServerError.is(error) ? {
131
+ const wire = _chunk6HETNZJYcjs.TapServerError.is(error) ? {
128
132
  code: error.code,
129
133
  message: error.message,
130
134
  status: _nullishCoalesce(error.status, () => ( 400))
131
135
  } : {
132
- code: _chunkRMUWRESKcjs.SERVER_ERROR_CODES.INTERNAL_ERROR,
133
- message: _chunkRMUWRESKcjs.INTERNAL_MESSAGE,
136
+ code: _chunkA74HCO6Tcjs.SERVER_ERROR_CODES.INTERNAL_ERROR,
137
+ message: _chunkA74HCO6Tcjs.INTERNAL_MESSAGE,
134
138
  status: 500
135
139
  };
136
140
  response.writeHead(wire.status, { "content-type": "application/json" });
@@ -141,61 +145,6 @@ function toNode(handler) {
141
145
  };
142
146
  }
143
147
 
144
- // src/server/events.ts
145
- var DEFAULT_BASE_URL = "https://api.tappify.ai";
146
- function toHex(buffer) {
147
- return Array.from(new Uint8Array(buffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
148
- }
149
- async function signWebhook(secret, body) {
150
- const encoder = new TextEncoder();
151
- const key = await crypto.subtle.importKey(
152
- "raw",
153
- encoder.encode(secret),
154
- { name: "HMAC", hash: "SHA-256" },
155
- false,
156
- ["sign"]
157
- );
158
- const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
159
- return toHex(signature);
160
- }
161
- async function rejectionCode(response) {
162
- try {
163
- const payload = await response.json();
164
- return _chunkRMUWRESKcjs.readText.call(void 0, _chunkRMUWRESKcjs.readRecord.call(void 0, payload, "error"), "code");
165
- } catch (e) {
166
- return null;
167
- }
168
- }
169
- async function sendEvent(name, payload, options) {
170
- const envelope = {
171
- event: name,
172
- source: options.extensionId,
173
- occurredAt: _nullishCoalesce(options.occurredAt, () => ( (/* @__PURE__ */ new Date()).toISOString())),
174
- dedupeKey: _nullishCoalesce(options.dedupeKey, () => ( crypto.randomUUID())),
175
- payload: { ...payload }
176
- };
177
- if (options.build !== void 0) envelope.build = options.build;
178
- const body = JSON.stringify(envelope);
179
- const signature = await signWebhook(options.secret, body);
180
- const url = _nullishCoalesce(options.endpoint, () => ( `${_nullishCoalesce(options.baseUrl, () => ( DEFAULT_BASE_URL))}/api/v1/extensions/hooks/${options.extensionId}/${options.installId}`));
181
- const send = _nullishCoalesce(options.fetch, () => ( fetch));
182
- const response = await send(url, {
183
- method: "POST",
184
- headers: {
185
- "content-type": "application/json",
186
- "x-tappify-signature": signature
187
- },
188
- body
189
- });
190
- if (!response.ok) {
191
- const rejection = await rejectionCode(response);
192
- throw new (0, _chunk2DBSVKAUcjs.TapServerError)(
193
- _chunkRMUWRESKcjs.SERVER_ERROR_CODES.WEBHOOK_REJECTED,
194
- `Tappify answered ${String(response.status)}${rejection === null ? "" : ` (${rejection})`} for ${name}. Check the inbound signing secret on the extension's Server page, and that the event is declared under contributes.webhooks.`,
195
- response.status
196
- );
197
- }
198
- }
199
148
 
200
149
 
201
150
 
@@ -206,5 +155,5 @@ async function sendEvent(name, payload, options) {
206
155
 
207
156
 
208
157
 
209
- exports.DEFAULT_JWKS_URL = _chunkRMUWRESKcjs.DEFAULT_JWKS_URL; exports.SERVER_ERROR_CODES = _chunkRMUWRESKcjs.SERVER_ERROR_CODES; exports.TapServerError = _chunk2DBSVKAUcjs.TapServerError; exports.createTappifyHandler = _chunkRMUWRESKcjs.createTappifyHandler; exports.sendEvent = sendEvent; exports.signWebhook = signWebhook; exports.toExpress = toExpress; exports.toNode = toNode; exports.verifyTappifyToken = _chunkRMUWRESKcjs.verifyTappifyToken;
158
+ exports.DEFAULT_JWKS_URL = _chunkA74HCO6Tcjs.DEFAULT_JWKS_URL; exports.SERVER_ERROR_CODES = _chunkA74HCO6Tcjs.SERVER_ERROR_CODES; exports.TapServerError = _chunk6HETNZJYcjs.TapServerError; exports.createTappifyHandler = _chunkA74HCO6Tcjs.createTappifyHandler; exports.sendEvent = _chunkEB5XWQAUcjs.sendEvent; exports.sendWorkEvent = _chunkEB5XWQAUcjs.sendWorkEvent; exports.signWebhook = _chunkEB5XWQAUcjs.signWebhook; exports.toExpress = toExpress; exports.toNode = toNode; exports.verifyTappifyToken = _chunkA74HCO6Tcjs.verifyTappifyToken;
210
159
  //# sourceMappingURL=server.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/vulakh/Desktop/Work/Tappify/extension-sdk/dist/server.cjs","../src/server/adapters.ts","../src/server/events.ts"],"names":[],"mappings":"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","file":"/Users/vulakh/Desktop/Work/Tappify/extension-sdk/dist/server.cjs","sourcesContent":[null,"import type { IncomingMessage, ServerResponse } from 'node:http';\nimport { TapServerError } from '../client/errors';\nimport { SERVER_ERROR_CODES } from './codes';\nimport type { TappifyFetchHandler } from './handler';\nimport { INTERNAL_MESSAGE, wireError } from './wire';\n\n/**\n * The part of a Node readable stream the adapters use, so neither adapter has to\n * name a framework's own request type.\n */\nexport interface BodyStream {\n /** Each chunk of the body, in order. */\n on(event: 'data', listener: (chunk: Uint8Array) => void): unknown;\n /** The body is complete. */\n on(event: 'end', listener: () => void): unknown;\n /** The stream failed; the adapter rejects with the error. */\n on(event: 'error', listener: (error: Error) => void): unknown;\n}\n\n/** The part of an Express request `toExpress` reads. */\nexport interface ExpressLikeRequest {\n method: string;\n /** Preferred over `url`, so a router-mounted path keeps its prefix. */\n originalUrl?: string;\n url?: string;\n headers: Record<string, string | string[] | undefined>;\n /** A parsed body from `express.json()`, or a raw string. */\n body?: unknown;\n /** Used as the scheme of the url handed to the handler. Defaults to `https`. */\n protocol?: string;\n /** Present when no parser has consumed the stream, which is then read as-is. */\n on?: BodyStream['on'];\n}\n\n/** The part of an Express response `toExpress` writes. */\nexport interface ExpressLikeResponse {\n /** Sets the status and returns the response, so the call chains into `send`. */\n status(code: number): ExpressLikeResponse;\n /** Called once per header the handler's response carries. */\n setHeader(name: string, value: string): void;\n /** Called once, with the handler's response body as text. */\n send(body: string): void;\n}\n\n/** What `toExpress` returns: an Express middleware over the three arguments. */\nexport type ExpressMiddleware = (\n request: ExpressLikeRequest,\n response: ExpressLikeResponse,\n next: (error?: unknown) => void,\n) => void;\n\n/** What `toNode` returns: a listener for `http.createServer`. */\nexport type NodeRequestListener = (\n request: IncomingMessage,\n response: ServerResponse,\n) => void;\n\nfunction toHeaders(\n raw: Record<string, string | string[] | undefined>,\n): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(raw)) {\n if (typeof value === 'string') headers.set(name, value);\n else if (Array.isArray(value)) headers.set(name, value.join(','));\n }\n return headers;\n}\n\nfunction originOf(\n headers: Record<string, string | string[] | undefined>,\n protocol: string,\n): string {\n const host = headers.host;\n return `${protocol}://${typeof host === 'string' ? host : 'localhost'}`;\n}\n\nfunction hasBody(method: string): boolean {\n return method !== 'GET' && method !== 'HEAD';\n}\n\nfunction readStream(stream: BodyStream): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const chunks: Uint8Array[] = [];\n let size = 0;\n\n stream.on('data', chunk => {\n chunks.push(chunk);\n size += chunk.byteLength;\n });\n\n stream.on('end', () => {\n const body = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n resolve(body.buffer);\n });\n\n stream.on('error', reject);\n });\n}\n\nfunction isReadable(\n request: ExpressLikeRequest,\n): request is ExpressLikeRequest & BodyStream {\n return typeof request.on === 'function';\n}\n\nasync function expressBody(\n request: ExpressLikeRequest,\n): Promise<BodyInit | undefined> {\n if (typeof request.body === 'string') return request.body;\n if (request.body !== undefined && request.body !== null) {\n return JSON.stringify(request.body);\n }\n\n if (!isReadable(request)) {\n throw new TapServerError(\n SERVER_ERROR_CODES.BODY_INVALID,\n 'The Express request carried no parsed body and no readable stream, so the call would have run against an empty body. Mount express.json() before the Tappify middleware, or mount the middleware before any parser that consumes the stream.',\n 400,\n );\n }\n\n const bytes = await readStream(request);\n return bytes.byteLength > 0 ? bytes : undefined;\n}\n\nasync function write(\n response: ExpressLikeResponse,\n webResponse: Response,\n): Promise<void> {\n webResponse.headers.forEach((value, name) => {\n response.setHeader(name, value);\n });\n response.status(webResponse.status).send(await webResponse.text());\n}\n\n/**\n * Wraps the handler as Express middleware, for a server that speaks Express\n * rather than `fetch`.\n *\n * @remarks\n * It builds the url from `originalUrl` and the `host` header, forwards every\n * header, and takes the body from `request.body` when a parser has already read\n * it or from the stream when none has. A request with neither — a parser that\n * consumed the stream without leaving a body — fails with a `TapServerError`\n * carrying `TAP_BODY_INVALID` rather than calling your handler against an empty\n * body, so mount `express.json()` before this middleware or mount this one first.\n * A `TapServerError` is written as the handler's own JSON error; anything else\n * goes to `next`, so your own error middleware sees it.\n *\n * @example\n * ```ts\n * import { createTappifyHandler, toExpress } from '@tappify/extension-sdk/server';\n *\n * const middleware = toExpress(\n * createTappifyHandler({ extensionId: 'starter', health: () => ({ ok: true }) }),\n * );\n * ```\n */\nexport function toExpress(handler: TappifyFetchHandler): ExpressMiddleware {\n return (request, response, next) => {\n void (async () => {\n const url = new URL(\n request.originalUrl ?? request.url ?? '/',\n originOf(request.headers, request.protocol ?? 'https'),\n );\n\n const init: RequestInit = {\n method: request.method,\n headers: toHeaders(request.headers),\n };\n\n if (hasBody(request.method)) {\n init.body = await expressBody(request);\n }\n\n await write(response, await handler(new Request(url, init)));\n })().catch((error: unknown) => {\n if (TapServerError.is(error)) {\n void write(\n response,\n wireError(error.code, error.message, error.status ?? 400),\n );\n return;\n }\n next(error);\n });\n };\n}\n\n/**\n * Wraps the handler as a listener for Node's own `http.createServer`, with no\n * framework in between.\n *\n * @remarks\n * It builds the url from `request.url` and the `host` header over `http`, forwards\n * every header, and reads the body straight off the stream, so nothing may have\n * consumed it first. It answers the whole response itself: a `TapServerError`\n * becomes its own code and status, and anything else becomes 500\n * `TAP_INTERNAL_ERROR`. Put it behind a TLS-terminating proxy in production; the\n * scheme it builds is only what the handler sees in `request.url`.\n *\n * @example\n * ```ts\n * import { createTappifyHandler, toNode } from '@tappify/extension-sdk/server';\n * import { createServer } from 'node:http';\n *\n * const listener = toNode(\n * createTappifyHandler({ extensionId: 'starter', health: () => ({ ok: true }) }),\n * );\n * const server = createServer(listener);\n * ```\n */\nexport function toNode(handler: TappifyFetchHandler): NodeRequestListener {\n return (request, response) => {\n void (async () => {\n const method = request.method ?? 'GET';\n const url = new URL(\n request.url ?? '/',\n originOf(request.headers, 'http'),\n );\n\n const init: RequestInit = {\n method,\n headers: toHeaders(request.headers),\n };\n\n if (hasBody(method)) {\n const bytes = await readStream(request);\n if (bytes.byteLength > 0) init.body = bytes;\n }\n\n const webResponse = await handler(new Request(url, init));\n const headers: Record<string, string> = {};\n webResponse.headers.forEach((value, name) => {\n headers[name] = value;\n });\n\n response.writeHead(webResponse.status, headers);\n response.end(await webResponse.text());\n })().catch((error: unknown) => {\n const wire = TapServerError.is(error)\n ? {\n code: error.code,\n message: error.message,\n status: error.status ?? 400,\n }\n : {\n code: SERVER_ERROR_CODES.INTERNAL_ERROR,\n message: INTERNAL_MESSAGE,\n status: 500,\n };\n\n response.writeHead(wire.status, { 'content-type': 'application/json' });\n response.end(\n JSON.stringify({ error: { code: wire.code, message: wire.message } }),\n );\n });\n };\n}\n","import { TapServerError } from '../client/errors';\nimport type { WebhookName, WebhookPayload } from '../client/types';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { readRecord, readText } from './wire';\n\n/** The body `sendEvent` posts to Tappify, and the body the signature covers. */\nexport interface WebhookEnvelope {\n /** The declared webhook name. */\n event: string;\n /** The extension id the event came from. */\n source: string;\n /** When the event happened, as an ISO timestamp. */\n occurredAt: string;\n /** The build the event belongs to, when it belongs to one. */\n build?: string;\n /** The key Tappify deduplicates on, so a retry is not counted twice. */\n dedupeKey: string;\n payload: Record<string, unknown>;\n}\n\n/** The third argument of `sendEvent`. */\nexport interface SendEventOptions {\n /** Which install the event belongs to. */\n installId: string;\n extensionId: string;\n /** The inbound signing secret from the extension's Server page. */\n secret: string;\n /** A full url to post to, which replaces `baseUrl` and the built path. */\n endpoint?: string;\n /** The Tappify origin to build the path under. Defaults to production. */\n baseUrl?: string;\n /** Defaults to a fresh UUID, so give your own to make a retry idempotent. */\n dedupeKey?: string;\n build?: string;\n /** Defaults to now, in ISO form. */\n occurredAt?: string;\n /** A `fetch` of your own, for a test or a proxy. Defaults to the global one. */\n fetch?: typeof fetch;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.tappify.ai';\n\nfunction toHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map(byte => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Returns the hex HMAC-SHA-256 of a raw body under a shared secret.\n *\n * @remarks\n * This is the value `sendEvent` sends as `X-Tappify-Signature`, computed over the\n * exact bytes of the body it posts. Call it directly only when you build the\n * request yourself, and sign the serialised string you send rather than\n * re-serialising the object, because a different key order is a different\n * signature. Uses Web Crypto, so it runs on Node, Cloudflare Workers and Vercel\n * alike.\n *\n * @example\n * ```ts\n * import { signWebhook } from '@tappify/extension-sdk/server';\n *\n * async function signed(body: string): Promise<Record<string, string>> {\n * return { 'x-tappify-signature': await signWebhook('whsec_test', body) };\n * }\n * ```\n */\nexport async function signWebhook(\n secret: string,\n body: string,\n): Promise<string> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(body));\n return toHex(signature);\n}\n\nasync function rejectionCode(response: Response): Promise<string | null> {\n try {\n const payload: unknown = await response.json();\n return readText(readRecord(payload, 'error'), 'code');\n } catch {\n return null;\n }\n}\n\n/**\n * Posts one of your declared webhook events to Tappify, signed with the install's\n * inbound secret.\n *\n * @remarks\n * The name and the payload are narrowed by the `TapWebhookMap` augmentation\n * `tappify extension types` writes. It builds the envelope, signs the serialised\n * body and posts it to the install's hook path under `baseUrl`, or to `endpoint`\n * when you give one. A response Tappify did not accept rejects with a\n * `TapServerError` whose `code` is always `TAP_WEBHOOK_REJECTED`, carrying the HTTP\n * status, and with Tappify's own code in the message text rather than in `code`;\n * the two usual causes are the wrong signing\n * secret and an event the manifest does not declare. Pass your own `dedupeKey` to\n * make a retry idempotent, because the default is a fresh UUID.\n *\n * @example\n * ```ts\n * import { sendEvent } from '@tappify/extension-sdk/server';\n *\n * async function reportAnomaly(installId: string, why: string): Promise<void> {\n * await sendEvent('anomaly.detected', { why }, {\n * installId,\n * extensionId: 'starter',\n * secret: process.env.TAPPIFY_INBOUND_SECRET ?? '',\n * dedupeKey: `anomaly-${installId}-${why}`,\n * });\n * }\n * ```\n */\nexport async function sendEvent<E extends WebhookName>(\n name: E,\n payload: WebhookPayload<E>,\n options: SendEventOptions,\n): Promise<void> {\n const envelope: WebhookEnvelope = {\n event: name,\n source: options.extensionId,\n occurredAt: options.occurredAt ?? new Date().toISOString(),\n dedupeKey: options.dedupeKey ?? crypto.randomUUID(),\n payload: { ...payload },\n };\n\n if (options.build !== undefined) envelope.build = options.build;\n\n const body = JSON.stringify(envelope);\n const signature = await signWebhook(options.secret, body);\n const url =\n options.endpoint ??\n `${options.baseUrl ?? DEFAULT_BASE_URL}/api/v1/extensions/hooks/${options.extensionId}/${options.installId}`;\n\n const send = options.fetch ?? fetch;\n const response = await send(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n const rejection = await rejectionCode(response);\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for ${name}. Check the inbound signing secret on the extension's Server page, and that the event is declared under contributes.webhooks.`,\n response.status,\n );\n }\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/server.cjs","../src/server/adapters.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACA;ACsCA,SAAS,SAAA,CACP,GAAA,EACS;AACT,EAAA,MAAM,QAAA,EAAU,IAAI,OAAA,CAAQ,CAAA;AAC5B,EAAA,IAAA,CAAA,MAAW,CAAC,IAAA,EAAM,KAAK,EAAA,GAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC/C,IAAA,GAAA,CAAI,OAAO,MAAA,IAAU,QAAA,EAAU,OAAA,CAAQ,GAAA,CAAI,IAAA,EAAM,KAAK,CAAA;AAAA,IAAA,KAAA,GAAA,CAC7C,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAA,CAAQ,GAAA,CAAI,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,EAClE;AACA,EAAA,OAAO,OAAA;AACT;AAEA,SAAS,QAAA,CACP,OAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,KAAA,EAAO,OAAA,CAAQ,IAAA;AACrB,EAAA,OAAO,CAAA,EAAA;AACT;AAES;AACA,EAAA;AACT;AAES;AACA,EAAA;AACC,IAAA;AACF,IAAA;AAEJ,IAAA;AACE,MAAA;AACA,MAAA;AACD,IAAA;AAED,IAAA;AACE,MAAA;AACI,MAAA;AACJ,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACD,IAAA;AAED,IAAA;AACD,EAAA;AACH;AAES;AAGA,EAAA;AACT;AAEA;AAGM,EAAA;AACA,EAAA;AACF,IAAA;AACF,EAAA;AAEK,EAAA;AACG,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACC,EAAA;AACT;AAEA;AAIE,EAAA;AACE,IAAA;AACD,EAAA;AACD,EAAA;AACF;AAyBgB;AACN,EAAA;AACA,IAAA;AACJ,MAAA;AACE,0CAAA;AACA,QAAA;AACF,MAAA;AAEA,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AAEI,MAAA;AACF,QAAA;AACF,MAAA;AAEA,MAAA;AACG,IAAA;AACC,MAAA;AACF,QAAA;AACE,UAAA;AACA,UAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACD,IAAA;AACH,EAAA;AACF;AAyBgB;AACN,EAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACE,yBAAA;AACA,QAAA;AACF,MAAA;AAEA,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AAEI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AAEA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACD,MAAA;AAED,MAAA;AACA,MAAA;AACG,IAAA;AACH,MAAA;AAEM,QAAA;AACA,QAAA;AACA,QAAA;AAEF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AAEJ,MAAA;AACA,MAAA;AACE,QAAA;AACF,MAAA;AACD,IAAA;AACH,EAAA;AACF;ADrHU;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/extension-sdk/extension-sdk/dist/server.cjs","sourcesContent":[null,"import type { IncomingMessage, ServerResponse } from 'node:http';\nimport { TapServerError } from '../client/errors';\nimport { SERVER_ERROR_CODES } from './codes';\nimport type { TappifyFetchHandler } from './handler';\nimport { INTERNAL_MESSAGE, wireError } from './wire';\n\n/**\n * The part of a Node readable stream the adapters use, so neither adapter has to\n * name a framework's own request type.\n */\nexport interface BodyStream {\n /** Each chunk of the body, in order. */\n on(event: 'data', listener: (chunk: Uint8Array) => void): unknown;\n /** The body is complete. */\n on(event: 'end', listener: () => void): unknown;\n /** The stream failed; the adapter rejects with the error. */\n on(event: 'error', listener: (error: Error) => void): unknown;\n}\n\n/** The part of an Express request `toExpress` reads. */\nexport interface ExpressLikeRequest {\n method: string;\n /** Preferred over `url`, so a router-mounted path keeps its prefix. */\n originalUrl?: string;\n url?: string;\n headers: Record<string, string | string[] | undefined>;\n /** A parsed body from `express.json()`, or a raw string. */\n body?: unknown;\n /** Used as the scheme of the url handed to the handler. Defaults to `https`. */\n protocol?: string;\n /** Present when no parser has consumed the stream, which is then read as-is. */\n on?: BodyStream['on'];\n}\n\n/** The part of an Express response `toExpress` writes. */\nexport interface ExpressLikeResponse {\n /** Sets the status and returns the response, so the call chains into `send`. */\n status(code: number): ExpressLikeResponse;\n /** Called once per header the handler's response carries. */\n setHeader(name: string, value: string): void;\n /** Called once, with the handler's response body as text. */\n send(body: string): void;\n}\n\n/** What `toExpress` returns: an Express middleware over the three arguments. */\nexport type ExpressMiddleware = (\n request: ExpressLikeRequest,\n response: ExpressLikeResponse,\n next: (error?: unknown) => void,\n) => void;\n\n/** What `toNode` returns: a listener for `http.createServer`. */\nexport type NodeRequestListener = (\n request: IncomingMessage,\n response: ServerResponse,\n) => void;\n\nfunction toHeaders(\n raw: Record<string, string | string[] | undefined>,\n): Headers {\n const headers = new Headers();\n for (const [name, value] of Object.entries(raw)) {\n if (typeof value === 'string') headers.set(name, value);\n else if (Array.isArray(value)) headers.set(name, value.join(','));\n }\n return headers;\n}\n\nfunction originOf(\n headers: Record<string, string | string[] | undefined>,\n protocol: string,\n): string {\n const host = headers.host;\n return `${protocol}://${typeof host === 'string' ? host : 'localhost'}`;\n}\n\nfunction hasBody(method: string): boolean {\n return method !== 'GET' && method !== 'HEAD';\n}\n\nfunction readStream(stream: BodyStream): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const chunks: Uint8Array[] = [];\n let size = 0;\n\n stream.on('data', chunk => {\n chunks.push(chunk);\n size += chunk.byteLength;\n });\n\n stream.on('end', () => {\n const body = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n resolve(body.buffer);\n });\n\n stream.on('error', reject);\n });\n}\n\nfunction isReadable(\n request: ExpressLikeRequest,\n): request is ExpressLikeRequest & BodyStream {\n return typeof request.on === 'function';\n}\n\nasync function expressBody(\n request: ExpressLikeRequest,\n): Promise<BodyInit | undefined> {\n if (typeof request.body === 'string') return request.body;\n if (request.body !== undefined && request.body !== null) {\n return JSON.stringify(request.body);\n }\n\n if (!isReadable(request)) {\n throw new TapServerError(\n SERVER_ERROR_CODES.BODY_INVALID,\n 'The Express request carried no parsed body and no readable stream, so the call would have run against an empty body. Mount express.json() before the Tappify middleware, or mount the middleware before any parser that consumes the stream.',\n 400,\n );\n }\n\n const bytes = await readStream(request);\n return bytes.byteLength > 0 ? bytes : undefined;\n}\n\nasync function write(\n response: ExpressLikeResponse,\n webResponse: Response,\n): Promise<void> {\n webResponse.headers.forEach((value, name) => {\n response.setHeader(name, value);\n });\n response.status(webResponse.status).send(await webResponse.text());\n}\n\n/**\n * Wraps the handler as Express middleware, for a server that speaks Express\n * rather than `fetch`.\n *\n * @remarks\n * It builds the url from `originalUrl` and the `host` header, forwards every\n * header, and takes the body from `request.body` when a parser has already read\n * it or from the stream when none has. A request with neither — a parser that\n * consumed the stream without leaving a body — fails with a `TapServerError`\n * carrying `TAP_BODY_INVALID` rather than calling your handler against an empty\n * body, so mount `express.json()` before this middleware or mount this one first.\n * A `TapServerError` is written as the handler's own JSON error; anything else\n * goes to `next`, so your own error middleware sees it.\n *\n * @example\n * ```ts\n * import { createTappifyHandler, toExpress } from '@tappify/extension-sdk/server';\n *\n * const middleware = toExpress(\n * createTappifyHandler({ extensionId: 'starter', health: () => ({ ok: true }) }),\n * );\n * ```\n */\nexport function toExpress(handler: TappifyFetchHandler): ExpressMiddleware {\n return (request, response, next) => {\n void (async () => {\n const url = new URL(\n request.originalUrl ?? request.url ?? '/',\n originOf(request.headers, request.protocol ?? 'https'),\n );\n\n const init: RequestInit = {\n method: request.method,\n headers: toHeaders(request.headers),\n };\n\n if (hasBody(request.method)) {\n init.body = await expressBody(request);\n }\n\n await write(response, await handler(new Request(url, init)));\n })().catch((error: unknown) => {\n if (TapServerError.is(error)) {\n void write(\n response,\n wireError(error.code, error.message, error.status ?? 400),\n );\n return;\n }\n next(error);\n });\n };\n}\n\n/**\n * Wraps the handler as a listener for Node's own `http.createServer`, with no\n * framework in between.\n *\n * @remarks\n * It builds the url from `request.url` and the `host` header over `http`, forwards\n * every header, and reads the body straight off the stream, so nothing may have\n * consumed it first. It answers the whole response itself: a `TapServerError`\n * becomes its own code and status, and anything else becomes 500\n * `TAP_INTERNAL_ERROR`. Put it behind a TLS-terminating proxy in production; the\n * scheme it builds is only what the handler sees in `request.url`.\n *\n * @example\n * ```ts\n * import { createTappifyHandler, toNode } from '@tappify/extension-sdk/server';\n * import { createServer } from 'node:http';\n *\n * const listener = toNode(\n * createTappifyHandler({ extensionId: 'starter', health: () => ({ ok: true }) }),\n * );\n * const server = createServer(listener);\n * ```\n */\nexport function toNode(handler: TappifyFetchHandler): NodeRequestListener {\n return (request, response) => {\n void (async () => {\n const method = request.method ?? 'GET';\n const url = new URL(\n request.url ?? '/',\n originOf(request.headers, 'http'),\n );\n\n const init: RequestInit = {\n method,\n headers: toHeaders(request.headers),\n };\n\n if (hasBody(method)) {\n const bytes = await readStream(request);\n if (bytes.byteLength > 0) init.body = bytes;\n }\n\n const webResponse = await handler(new Request(url, init));\n const headers: Record<string, string> = {};\n webResponse.headers.forEach((value, name) => {\n headers[name] = value;\n });\n\n response.writeHead(webResponse.status, headers);\n response.end(await webResponse.text());\n })().catch((error: unknown) => {\n const wire = TapServerError.is(error)\n ? {\n code: error.code,\n message: error.message,\n status: error.status ?? 400,\n }\n : {\n code: SERVER_ERROR_CODES.INTERNAL_ERROR,\n message: INTERNAL_MESSAGE,\n status: 500,\n };\n\n response.writeHead(wire.status, { 'content-type': 'application/json' });\n response.end(\n JSON.stringify({ error: { code: wire.code, message: wire.message } }),\n );\n });\n };\n}\n"]}
package/dist/server.d.cts CHANGED
@@ -1,10 +1,10 @@
1
- export { c as TapServerError } from './errors-COsBh7kd.cjs';
2
- import { ad as WebhookName, ae as WebhookPayload } from './types-DrpQSv_2.cjs';
3
- export { A as ActionId, k as ActionInput, w as TapCredentialValues, a as TapFilters, c as TapHostEvents, $ as TapSettingsValues, ab as ToolId, ac as ToolInput } from './types-DrpQSv_2.cjs';
1
+ export { c as TapServerError } from './errors-Cqh0wrqt.cjs';
2
+ import { ad as WebhookName, ae as WebhookPayload } from './types-I1JOZyI_.cjs';
3
+ export { A as ActionId, k as ActionInput, w as TapCredentialValues, a as TapFilters, c as TapHostEvents, $ as TapSettingsValues, ab as ToolId, ac as ToolInput } from './types-I1JOZyI_.cjs';
4
4
  import { IncomingMessage, ServerResponse } from 'node:http';
5
- import { T as TappifyFetchHandler, V as VerifyTokenOptions, a as TappifyClaims } from './handler-kU9Jf36K.cjs';
6
- export { C as ContextBlock, M as MentionItems, b as MetricsInput, c as MetricsResponse, d as MetricsSeries, e as TappifyCallContext, f as TappifyDocuments, g as TappifyHandlerOptions, h as TappifyHealth, i as TappifyInstall, j as TappifyRequest, k as TappifyRouteHandler, W as WorkOperation, l as createTappifyHandler } from './handler-kU9Jf36K.cjs';
7
- import './constants-DJ_KgZpN.cjs';
5
+ import { T as TappifyFetchHandler, V as VerifyTokenOptions, a as TappifyClaims } from './work-events-Dxe7Djdy.cjs';
6
+ export { C as ContextBlock, L as LegacyWorkHandlers, M as MentionItems, b as MetricsInput, c as MetricsResponse, d as MetricsSeries, R as ReviewAttachmentDescriptor, e as ReviewImageAttachmentDescriptor, f as ReviewLinkAttachmentDescriptor, g as ReviewThreadSource, S as SendWorkEventOptions, h as TappifyCallContext, i as TappifyDocuments, j as TappifyHandlerOptions, k as TappifyHealth, l as TappifyInstall, m as TappifyRequest, n as TappifyRouteHandler, W as WorkAttachInput, o as WorkAttachResult, p as WorkCommentInput, q as WorkCommentResult, r as WorkConfigureInput, s as WorkConfigureResult, t as WorkContainer, u as WorkCreateField, v as WorkCreateFieldValues, w as WorkCreateInput, x as WorkDescribeContainerInput, y as WorkDescribeContainerResult, z as WorkDisconnectInput, A as WorkDisconnectResult, B as WorkEvent, D as WorkHydrateEventInput, E as WorkHydrateEventResult, F as WorkHydratedCommentCreatedEvent, G as WorkHydratedEvent, H as WorkHydratedEventBase, I as WorkHydratedItemDeletedEvent, J as WorkHydratedStatusChangedEvent, K as WorkHydrationReferenceEvent, N as WorkItemLinkResult, O as WorkItemType, P as WorkLinkedItemInput, Q as WorkListContainersInput, U as WorkListContainersResult, X as WorkMutationInput, Y as WorkOperation, Z as WorkOperationMap, _ as WorkSetStatusInput, $ as WorkSetStatusResult, a0 as WorkStage, a1 as WorkStatus, a2 as createTappifyHandler, a3 as sendWorkEvent } from './work-events-Dxe7Djdy.cjs';
7
+ import './constants-BAimnqOK.cjs';
8
8
  import 'jose';
9
9
 
10
10
  /**
package/dist/server.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export { c as TapServerError } from './errors-COsBh7kd.js';
2
- import { ad as WebhookName, ae as WebhookPayload } from './types-DDAGLpov.js';
3
- export { A as ActionId, k as ActionInput, w as TapCredentialValues, a as TapFilters, c as TapHostEvents, $ as TapSettingsValues, ab as ToolId, ac as ToolInput } from './types-DDAGLpov.js';
1
+ export { c as TapServerError } from './errors-Cqh0wrqt.js';
2
+ import { ad as WebhookName, ae as WebhookPayload } from './types-BYGcp5pZ.js';
3
+ export { A as ActionId, k as ActionInput, w as TapCredentialValues, a as TapFilters, c as TapHostEvents, $ as TapSettingsValues, ab as ToolId, ac as ToolInput } from './types-BYGcp5pZ.js';
4
4
  import { IncomingMessage, ServerResponse } from 'node:http';
5
- import { T as TappifyFetchHandler, V as VerifyTokenOptions, a as TappifyClaims } from './handler-CZIlZ5vu.js';
6
- export { C as ContextBlock, M as MentionItems, b as MetricsInput, c as MetricsResponse, d as MetricsSeries, e as TappifyCallContext, f as TappifyDocuments, g as TappifyHandlerOptions, h as TappifyHealth, i as TappifyInstall, j as TappifyRequest, k as TappifyRouteHandler, W as WorkOperation, l as createTappifyHandler } from './handler-CZIlZ5vu.js';
7
- import './constants-DJ_KgZpN.js';
5
+ import { T as TappifyFetchHandler, V as VerifyTokenOptions, a as TappifyClaims } from './work-events-DB3WR468.js';
6
+ export { C as ContextBlock, L as LegacyWorkHandlers, M as MentionItems, b as MetricsInput, c as MetricsResponse, d as MetricsSeries, R as ReviewAttachmentDescriptor, e as ReviewImageAttachmentDescriptor, f as ReviewLinkAttachmentDescriptor, g as ReviewThreadSource, S as SendWorkEventOptions, h as TappifyCallContext, i as TappifyDocuments, j as TappifyHandlerOptions, k as TappifyHealth, l as TappifyInstall, m as TappifyRequest, n as TappifyRouteHandler, W as WorkAttachInput, o as WorkAttachResult, p as WorkCommentInput, q as WorkCommentResult, r as WorkConfigureInput, s as WorkConfigureResult, t as WorkContainer, u as WorkCreateField, v as WorkCreateFieldValues, w as WorkCreateInput, x as WorkDescribeContainerInput, y as WorkDescribeContainerResult, z as WorkDisconnectInput, A as WorkDisconnectResult, B as WorkEvent, D as WorkHydrateEventInput, E as WorkHydrateEventResult, F as WorkHydratedCommentCreatedEvent, G as WorkHydratedEvent, H as WorkHydratedEventBase, I as WorkHydratedItemDeletedEvent, J as WorkHydratedStatusChangedEvent, K as WorkHydrationReferenceEvent, N as WorkItemLinkResult, O as WorkItemType, P as WorkLinkedItemInput, Q as WorkListContainersInput, U as WorkListContainersResult, X as WorkMutationInput, Y as WorkOperation, Z as WorkOperationMap, _ as WorkSetStatusInput, $ as WorkSetStatusResult, a0 as WorkStage, a1 as WorkStatus, a2 as createTappifyHandler, a3 as sendWorkEvent } from './work-events-DB3WR468.js';
7
+ import './constants-BAimnqOK.js';
8
8
  import 'jose';
9
9
 
10
10
  /**