@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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vite-preview.tsx","../src/vite/preview/client.tsx","../package.json","../src/vite/preview/error-boundary.tsx","../src/vite/preview/model.ts","../src/vite/preview/styles.ts"],"sourcesContent":["import { createRoot } from 'react-dom/client';\nimport { LocalPreview, type LocalPreviewOptions } from './vite/preview/client';\n\nexport {\n componentFromModule,\n loadContribution,\n LocalPreview,\n} from './vite/preview/client';\nexport type {\n LocalPreviewOptions,\n LocalPreviewProps,\n PreviewLoadRequest,\n PreviewModuleLoader,\n} from './vite/preview/client';\n\nexport function mountLocalPreview({\n manifest,\n remote,\n mount,\n}: LocalPreviewOptions): () => void {\n const root = createRoot(mount);\n root.render(<LocalPreview manifest={manifest} remote={remote} />);\n return () => root.unmount();\n}\n","import type { ModuleFederation } from '@module-federation/runtime';\nimport * as TanstackQuery from '@tanstack/react-query';\nimport * as React from 'react';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ComponentType,\n} from 'react';\nimport * as ReactDom from 'react-dom';\nimport * as JsxRuntime from 'react/jsx-runtime';\nimport packageJson from '../../../package.json';\nimport type { TapSettingsValues, TapSize } from '../../client/types';\nimport { TapHostProvider, createHostQueryClient } from '../../host/provider';\nimport * as TappifySdk from '../../index';\nimport { remoteName } from '../../manifest/define';\nimport type { ExtensionManifest } from '../../manifest/types';\nimport { fixtures } from '../../testing/fixtures';\nimport { createTapMock } from '../../testing/mock';\nimport { PreviewErrorBoundary } from './error-boundary';\nimport { previewProps, previewSelections } from './model';\nimport { previewStyles } from './styles';\n\nexport interface PreviewLoadRequest {\n extensionId: string;\n remote: string;\n expose: string;\n}\n\nexport type PreviewModuleLoader = (\n request: PreviewLoadRequest,\n) => Promise<unknown>;\n\nexport interface LocalPreviewOptions {\n manifest: ExtensionManifest;\n remote: string;\n mount: HTMLElement;\n}\n\nexport interface LocalPreviewProps {\n manifest: ExtensionManifest;\n remote: string;\n loader?: PreviewModuleLoader;\n}\n\ntype Theme = 'light' | 'dark';\ntype Viewport = 'desktop' | 'tablet' | 'mobile';\n\ntype LoadState =\n | { key: string; status: 'loading' }\n | {\n key: string;\n status: 'ready';\n Component: ComponentType<Record<string, unknown>>;\n styles: string[];\n }\n | { key: string; status: 'failed'; message: string };\n\nconst SINGLETON = { singleton: true, requiredVersion: false } as const;\nconst federationInstances = new Map<string, Promise<ModuleFederation>>();\nlet federationSequence = 0;\n\nasync function federationFor(\n extensionId: string,\n remote: string,\n): Promise<ModuleFederation> {\n const key = `${extensionId}|${remote}`;\n const existing = federationInstances.get(key);\n if (existing !== undefined) return existing;\n\n federationSequence += 1;\n const name = remoteName(extensionId);\n const created = import('@module-federation/runtime').then(\n ({ createInstance }) =>\n createInstance({\n name: `tappify_local_preview_${String(federationSequence)}`,\n remotes: [{ name, alias: name, entry: remote, type: 'module' }],\n shared: {\n react: {\n version: React.version,\n lib: () => React,\n shareConfig: SINGLETON,\n },\n 'react-dom': {\n version: ReactDom.version,\n lib: () => ReactDom,\n shareConfig: SINGLETON,\n },\n 'react/jsx-runtime': {\n version: React.version,\n lib: () => JsxRuntime,\n shareConfig: SINGLETON,\n },\n '@tanstack/react-query': {\n version: packageJson.peerDependencies[\n '@tanstack/react-query'\n ].replace(/^[^\\d]*/, ''),\n lib: () => TanstackQuery,\n shareConfig: SINGLETON,\n },\n '@tappify/extension-sdk': {\n version: packageJson.version,\n lib: () => TappifySdk,\n shareConfig: SINGLETON,\n },\n },\n }),\n );\n federationInstances.set(key, created);\n return created;\n}\n\nexport function componentFromModule(\n module: unknown,\n expose: string,\n): ComponentType<Record<string, unknown>> {\n const candidate =\n typeof module === 'object' && module !== null && 'default' in module\n ? (module as { default: unknown }).default\n : module;\n if (typeof candidate !== 'function') {\n throw new Error(\n `${expose} did not default-export a React component. Export the contribution component as default.`,\n );\n }\n return candidate as ComponentType<Record<string, unknown>>;\n}\n\nfunction stylesFromModule(module: unknown): string[] {\n if (\n typeof module !== 'object' ||\n module === null ||\n !('styles' in module) ||\n !Array.isArray(module.styles)\n ) {\n return [];\n }\n return module.styles.filter(\n (style): style is string => typeof style === 'string',\n );\n}\n\nexport async function loadContribution({\n extensionId,\n remote,\n expose,\n}: PreviewLoadRequest): Promise<unknown> {\n const federation = await federationFor(extensionId, remote);\n return federation.loadRemote(\n `${remoteName(extensionId)}/${expose.replace(/^\\.\\//, '')}`,\n );\n}\n\nfunction isExternalRemote(remote: string): boolean {\n try {\n const host = new URL(remote).hostname;\n return host !== 'localhost' && host !== '127.0.0.1' && host !== '[::1]';\n } catch {\n return true;\n }\n}\n\nfunction localProcedureStubs(\n manifest: ExtensionManifest,\n): Record<string, () => never> {\n return Object.fromEntries(\n Object.keys(manifest.server?.procedures ?? {}).map(name => [\n name,\n () => {\n throw new Error(\n 'This view needs live server data. Run tappify extension dev --live to connect it.',\n );\n },\n ]),\n );\n}\n\nfunction Message({\n title,\n children,\n}: {\n title: string;\n children: React.ReactNode;\n}) {\n return (\n <div className=\"tap-preview-message\" role=\"status\">\n <h2>{title}</h2>\n <span>{children}</span>\n </div>\n );\n}\n\ninterface PreviewShadowNodes {\n root: ShadowRoot;\n content: HTMLElement;\n}\n\nfunction PreviewSurface({\n portal,\n styles,\n children,\n}: {\n portal: HTMLElement;\n styles: string[];\n children: React.ReactNode;\n}) {\n const [nodes, setNodes] = useState<PreviewShadowNodes | null>(null);\n const attachHost = useCallback((host: HTMLDivElement | null): void => {\n if (host === null) return;\n const root = host.shadowRoot ?? host.attachShadow({ mode: 'open' });\n let content = root.querySelector<HTMLElement>('[data-tap-content]');\n if (content === null) {\n content = document.createElement('div');\n content.className = 'tap-root';\n content.setAttribute('data-tap-content', '');\n root.appendChild(content);\n }\n setNodes({ root, content });\n }, []);\n\n useEffect(() => {\n if (nodes === null) return;\n nodes.root.appendChild(portal);\n return () => portal.remove();\n }, [nodes, portal]);\n\n useEffect(() => {\n if (nodes === null) return;\n const injected: HTMLStyleElement[] = [];\n const inject = (kind: string, css: string): void => {\n const element = document.createElement('style');\n element.setAttribute('data-tap-style', kind);\n element.textContent = css;\n nodes.root.insertBefore(element, nodes.content);\n injected.push(element);\n };\n\n inject(\n 'preview',\n ':host { display: block; height: 100%; min-height: 0; overflow: auto; } .tap-root { box-sizing: border-box; height: 100%; min-height: 100%; padding: 24px; }',\n );\n for (const css of styles) inject('remote', css);\n return () => {\n for (const element of injected) element.remove();\n };\n }, [nodes, styles]);\n\n return (\n <div\n ref={attachHost}\n className=\"tap-preview-surface\"\n data-tap-preview-surface\n >\n {nodes === null ? null : ReactDom.createPortal(children, nodes.content)}\n </div>\n );\n}\n\nexport function LocalPreview({\n manifest,\n remote,\n loader = loadContribution,\n}: LocalPreviewProps) {\n const selections = useMemo(() => previewSelections(manifest), [manifest]);\n const [expose, setExpose] = useState(\n selections[0]?.contribution.expose ?? '',\n );\n const [theme, setTheme] = useState<Theme>('light');\n const [viewport, setViewport] = useState<Viewport>('desktop');\n const [size, setSize] = useState<TapSize>('slot');\n const [generation, setGeneration] = useState(0);\n const [settings, setSettings] = useState<TapSettingsValues>({});\n const [loaded, setLoaded] = useState<LoadState>({\n key: '',\n status: 'loading',\n });\n\n const selection =\n selections.find(item => item.contribution.expose === expose) ??\n selections[0];\n const contribution = selection?.contribution;\n const loadKey = `${remote}|${contribution?.expose ?? ''}`;\n\n useEffect(() => {\n if (contribution === undefined) return;\n let cancelled = false;\n setLoaded({ key: loadKey, status: 'loading' });\n void loader({\n extensionId: manifest.id,\n remote,\n expose: contribution.expose,\n })\n .then(module => {\n if (cancelled) return;\n setLoaded({\n key: loadKey,\n status: 'ready',\n Component: componentFromModule(module, contribution.expose),\n styles: stylesFromModule(module),\n });\n })\n .catch((error: unknown) => {\n if (cancelled) return;\n setLoaded({\n key: loadKey,\n status: 'failed',\n message:\n error instanceof Error\n ? error.message\n : 'The remote entry could not be loaded.',\n });\n });\n return () => {\n cancelled = true;\n };\n }, [contribution, loadKey, loader, manifest.id, remote]);\n\n const host = useMemo(() => {\n void generation;\n return createTapMock({\n extensionId: manifest.id,\n extensionName: manifest.name,\n project: fixtures.project.project,\n theme,\n size,\n server: localProcedureStubs(manifest),\n });\n }, [generation, manifest, size, theme]);\n const queryClient = useMemo(() => {\n void generation;\n return createHostQueryClient();\n }, [generation]);\n\n const current: LoadState =\n loaded.key === loadKey ? loaded : { key: loadKey, status: 'loading' };\n\n let content: React.ReactNode;\n if (contribution === undefined) {\n content = (\n <Message title=\"No UI contributions\">\n Run `tappify extension add` and choose a page, tab, widget, row action,\n or settings panel.\n </Message>\n );\n } else if (current.status === 'failed') {\n content = (\n <div className=\"tap-preview-message\" role=\"alert\">\n <strong>The remote entry could not load.</strong>\n <span>{current.message}</span>\n </div>\n );\n } else if (current.status === 'ready') {\n const Component = current.Component;\n const props = previewProps(contribution, {\n settings,\n onSettingsChange: setSettings,\n });\n content = (\n <TapHostProvider tap={host.tap} queryClient={queryClient}>\n <PreviewSurface portal={host.portal} styles={current.styles}>\n <PreviewErrorBoundary key={`${loadKey}|${String(generation)}`}>\n <Component {...props} />\n </PreviewErrorBoundary>\n </PreviewSurface>\n </TapHostProvider>\n );\n } else {\n content = (\n <Message title=\"Loading contribution\">Loading {selection.label}.</Message>\n );\n }\n\n return (\n <main className=\"tap-preview\" data-theme={theme}>\n <style>{previewStyles}</style>\n <header className=\"tap-preview-bar\">\n <div className=\"tap-preview-brand\">\n <img src={`/${manifest.icon.replace(/^\\.\\//, '')}`} alt=\"\" />\n <div>\n <strong>{manifest.name}</strong>\n <span>\n <i aria-hidden=\"true\" />\n {isExternalRemote(remote) ? 'External remote' : 'Local preview'}\n {' · Fixture data'}\n </span>\n </div>\n </div>\n <div className=\"tap-preview-controls\">\n {selections.length > 0 ? (\n <label className=\"tap-preview-field tap-preview-field--contribution\">\n <span>Contribution</span>\n <select\n value={contribution?.expose ?? ''}\n onChange={event => setExpose(event.target.value)}\n >\n {selections.map(item => (\n <option\n key={item.contribution.expose}\n value={item.contribution.expose}\n >\n {item.label}\n </option>\n ))}\n </select>\n </label>\n ) : null}\n <label className=\"tap-preview-field\">\n <span>Theme</span>\n <select\n value={theme}\n onChange={event => setTheme(event.target.value as Theme)}\n >\n <option value=\"light\">Light</option>\n <option value=\"dark\">Dark</option>\n </select>\n </label>\n <label className=\"tap-preview-field\">\n <span>Viewport</span>\n <select\n value={viewport}\n onChange={event => setViewport(event.target.value as Viewport)}\n >\n <option value=\"desktop\">Desktop</option>\n <option value=\"tablet\">Tablet</option>\n <option value=\"mobile\">Mobile</option>\n </select>\n </label>\n <label className=\"tap-preview-field\">\n <span>Surface</span>\n <select\n value={size}\n aria-label=\"Surface size\"\n onChange={event => setSize(event.target.value as TapSize)}\n >\n <option value=\"slot\">Slot</option>\n <option value=\"panel\">Panel</option>\n <option value=\"page\">Page</option>\n </select>\n </label>\n <button\n className=\"tap-preview-reset\"\n type=\"button\"\n aria-label=\"Reset fixtures\"\n onClick={() => {\n setSettings({});\n setGeneration(value => value + 1);\n }}\n >\n Reset\n </button>\n </div>\n </header>\n <section className=\"tap-preview-stage\">\n <div\n className=\"tap-preview-canvas\"\n data-viewport={viewport}\n data-size={size}\n aria-label=\"Preview canvas\"\n >\n {content}\n </div>\n </section>\n </main>\n );\n}\n","{\n \"name\": \"@tappify/extension-sdk\",\n \"version\": \"2.1.0-beta.1\",\n \"description\": \"Build extensions that run inside Tappify\",\n \"author\": \"Tappify Team\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.cjs\"\n },\n \"./manifest\": {\n \"types\": \"./dist/manifest.d.ts\",\n \"import\": \"./dist/manifest.mjs\",\n \"require\": \"./dist/manifest.cjs\"\n },\n \"./server\": {\n \"types\": \"./dist/server.d.ts\",\n \"import\": \"./dist/server.mjs\",\n \"require\": \"./dist/server.cjs\"\n },\n \"./host\": {\n \"types\": \"./dist/host.d.ts\",\n \"import\": \"./dist/host.mjs\",\n \"require\": \"./dist/host.cjs\"\n },\n \"./ui\": {\n \"types\": \"./dist/ui.d.ts\",\n \"import\": \"./dist/ui.mjs\",\n \"require\": \"./dist/ui.cjs\"\n },\n \"./vite\": {\n \"types\": \"./dist/vite.d.ts\",\n \"import\": \"./dist/vite.mjs\",\n \"require\": \"./dist/vite.cjs\"\n },\n \"./vite/preview\": {\n \"types\": \"./dist/vite-preview.d.ts\",\n \"import\": \"./dist/vite-preview.mjs\",\n \"require\": \"./dist/vite-preview.cjs\"\n },\n \"./vite/preview/bootstrap\": {\n \"types\": \"./dist/vite-preview-bootstrap.d.ts\",\n \"import\": \"./dist/vite-preview-bootstrap.mjs\",\n \"require\": \"./dist/vite-preview-bootstrap.cjs\"\n },\n \"./testing\": {\n \"types\": \"./dist/testing/index.d.ts\",\n \"import\": \"./dist/testing/index.mjs\",\n \"require\": \"./dist/testing/index.cjs\"\n },\n \"./testing/vitest\": {\n \"types\": \"./dist/testing/vitest.d.ts\",\n \"import\": \"./dist/testing/vitest.mjs\",\n \"require\": \"./dist/testing/vitest.cjs\"\n },\n \"./testing/jest\": {\n \"types\": \"./dist/testing/jest.d.ts\",\n \"import\": \"./dist/testing/jest.mjs\",\n \"require\": \"./dist/testing/jest.cjs\"\n },\n \"./codegen\": {\n \"types\": \"./dist/codegen.d.ts\",\n \"import\": \"./dist/codegen.mjs\",\n \"require\": \"./dist/codegen.cjs\"\n },\n \"./ui/styles.css\": \"./dist/ui/styles.css\",\n \"./schema/extension-v2.json\": \"./schema/extension-v2.json\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \">=20\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.ts\",\n \"sideEffects\": [\n \"*.css\",\n \"./dist/testing/vitest.*\",\n \"./dist/testing/jest.*\"\n ],\n \"files\": [\n \"dist\",\n \"schema\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/tappify-dev/extension-sdk.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/tappify-dev/extension-sdk/issues\"\n },\n \"homepage\": \"https://docs.tappify.ai/extensions\",\n \"keywords\": [\n \"tappify\",\n \"extension\",\n \"extension-sdk\",\n \"react\",\n \"module-federation\"\n ],\n \"scripts\": {\n \"build\": \"rm -rf dist && tsup && node scripts/copy-styles.mjs\",\n \"build:schema\": \"tsx scripts/build-json-schema.ts\",\n \"build:manifest-reference\": \"tsx scripts/build-manifest-reference.ts\",\n \"build:api-reference\": \"tsx scripts/build-api-reference.ts\",\n \"check:hosted-schema\": \"node scripts/check-hosted-schema.mjs\",\n \"docs:examples\": \"tsx scripts/check-tsdoc-examples.ts && tsc -p tsconfig.examples.json\",\n \"docs:validate\": \"typedoc --options typedoc.frontend.json --emit none && typedoc --options typedoc.server.json --emit none && typedoc --options typedoc.testing.json --emit none\",\n \"docs:reference\": \"pnpm docs:examples && pnpm docs:validate && pnpm build:schema && pnpm build:manifest-reference && pnpm build:api-reference\",\n \"dev\": \"tsup --watch\",\n \"type-check\": \"tsc --noEmit\",\n \"format\": \"prettier --write \\\"src/**/*.{ts,tsx}\\\" \\\"test/**/*.{ts,tsx}\\\" \\\"*.{json,md,mjs,cjs}\\\"\",\n \"format:check\": \"prettier --check \\\"src/**/*.{ts,tsx}\\\" \\\"test/**/*.{ts,tsx}\\\" \\\"*.{json,md,mjs,cjs}\\\"\",\n \"lint\": \"eslint \\\"{src,test}/**/*.{ts,tsx}\\\" --fix\",\n \"test\": \"vitest run --passWithNoTests --coverage\",\n \"test:watch\": \"vitest --watch\",\n \"prepare\": \"husky\",\n \"commit\": \"cz\",\n \"prepublishOnly\": \"npm run build && npm test\",\n \"spellcheck\": \"cspell \\\"src/**/*.{ts,tsx}\\\" \\\"test/**/*.{ts,tsx}\\\" \\\"*.{json,md,yml,mjs,cjs}\\\" \\\".github/**/*.yml\\\"\",\n \"semantic-release\": \"semantic-release\"\n },\n \"peerDependencies\": {\n \"@module-federation/vite\": \">=1.16.0\",\n \"@tanstack/react-query\": \"^5.100.0\",\n \"@testing-library/react\": \">=16.0.0\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\",\n \"vite\": \">=8.0.0\",\n \"vitest\": \">=3.0.0\"\n },\n \"peerDependenciesMeta\": {\n \"@module-federation/vite\": {\n \"optional\": true\n },\n \"@tanstack/react-query\": {\n \"optional\": true\n },\n \"@testing-library/react\": {\n \"optional\": true\n },\n \"react\": {\n \"optional\": true\n },\n \"react-dom\": {\n \"optional\": true\n },\n \"vite\": {\n \"optional\": true\n },\n \"vitest\": {\n \"optional\": true\n }\n },\n \"dependencies\": {\n \"@module-federation/runtime\": \"~2.5.1\",\n \"clsx\": \"^2.1.1\",\n \"jose\": \"^6.2.12\",\n \"json-schema-to-typescript\": \"^16.0.0\",\n \"zod\": \"^4.3.6\"\n },\n \"devDependencies\": {\n \"@commitlint/cli\": \"^20.4.3\",\n \"@commitlint/config-conventional\": \"^20.4.3\",\n \"@eslint/js\": \"^10.0.1\",\n \"@module-federation/vite\": \"~1.16.0\",\n \"@semantic-release/changelog\": \"^6.0.3\",\n \"@semantic-release/commit-analyzer\": \"^13.0.1\",\n \"@semantic-release/git\": \"^10.0.1\",\n \"@semantic-release/github\": \"^12.0.6\",\n \"@semantic-release/npm\": \"^13.1.5\",\n \"@semantic-release/release-notes-generator\": \"^14.1.0\",\n \"@tanstack/react-query\": \"^5.100.14\",\n \"@testing-library/jest-dom\": \"^6.6.3\",\n \"@testing-library/react\": \"^16.1.0\",\n \"@testing-library/user-event\": \"^14.5.2\",\n \"@types/json-schema\": \"^7.0.15\",\n \"@types/node\": \"^22.13.0\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.2.7\",\n \"@vitest/coverage-v8\": \"^3.0.0\",\n \"commitizen\": \"^4.3.1\",\n \"conventional-changelog-conventionalcommits\": \"^9.3.0\",\n \"cspell\": \"^9.7.0\",\n \"cz-conventional-changelog\": \"^3.3.0\",\n \"eslint\": \"^10.0.2\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-prettier\": \"^5.5.5\",\n \"eslint-plugin-react\": \"^7.37.0\",\n \"eslint-plugin-react-hooks\": \"^5.1.0\",\n \"globals\": \"^17.4.0\",\n \"happy-dom\": \"^17.0.0\",\n \"husky\": \"^9.1.7\",\n \"lint-staged\": \"^16.3.2\",\n \"prettier\": \"^3.8.1\",\n \"prettier-plugin-organize-imports\": \"^4.3.0\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\",\n \"semantic-release\": \"^25.0.3\",\n \"tsup\": \"^8.3.0\",\n \"tsx\": \"^4.20.0\",\n \"typedoc\": \"^0.28.20\",\n \"typedoc-plugin-markdown\": \"^4.13.0\",\n \"typescript\": \"^5.7.0\",\n \"typescript-eslint\": \"^8.56.1\",\n \"vite\": \"^8.0.14\",\n \"vitest\": \"^3.0.0\"\n },\n \"config\": {\n \"commitizen\": {\n \"path\": \"cz-conventional-changelog\",\n \"types\": {\n \"feat\": {\n \"description\": \"New feature\",\n \"title\": \"Features\"\n },\n \"fix\": {\n \"description\": \"Bug fix\",\n \"title\": \"Fixes\"\n },\n \"docs\": {\n \"description\": \"Documentation changes\",\n \"title\": \"Documentation\"\n },\n \"style\": {\n \"description\": \"Code style changes (formatting, etc.)\",\n \"title\": \"Styles\"\n },\n \"refactor\": {\n \"description\": \"Code refactoring\",\n \"title\": \"Refactoring\"\n },\n \"perf\": {\n \"description\": \"Performance improvements\",\n \"title\": \"Performance\"\n },\n \"test\": {\n \"description\": \"Adding or updating tests\",\n \"title\": \"Tests\"\n },\n \"build\": {\n \"description\": \"Build system or dependencies\",\n \"title\": \"Build\"\n },\n \"ci\": {\n \"description\": \"CI/CD changes\",\n \"title\": \"CI/CD\"\n },\n \"chore\": {\n \"description\": \"Chore\",\n \"title\": \"Chores\"\n },\n \"revert\": {\n \"description\": \"Revert previous commit\",\n \"title\": \"Reverts\"\n },\n \"wip\": {\n \"description\": \"Work in progress / continuing a feature\",\n \"title\": \"Work in Progress\"\n },\n \"cont\": {\n \"description\": \"Continue/complete a feature\",\n \"title\": \"Continue Feature\"\n },\n \"improve\": {\n \"description\": \"Small improvements or enhancements\",\n \"title\": \"Improvements\"\n }\n }\n }\n },\n \"lint-staged\": {\n \"*.{ts,tsx}\": [\n \"eslint --fix\",\n \"prettier --write\",\n \"cspell --no-must-find-files\"\n ],\n \"*.{json,md}\": [\n \"prettier --write\",\n \"cspell --no-must-find-files\"\n ]\n }\n}\n","import { Component, type ErrorInfo, type ReactNode } from 'react';\n\ninterface PreviewErrorBoundaryProps {\n children: ReactNode;\n}\n\ninterface PreviewErrorBoundaryState {\n error: Error | null;\n}\n\nexport class PreviewErrorBoundary extends Component<\n PreviewErrorBoundaryProps,\n PreviewErrorBoundaryState\n> {\n state: PreviewErrorBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error): PreviewErrorBoundaryState {\n return { error };\n }\n\n componentDidCatch(_error: Error, _info: ErrorInfo): void {\n // The visible diagnostic is the local preview's reporting boundary.\n }\n\n render(): ReactNode {\n if (this.state.error !== null) {\n return (\n <div className=\"tap-preview-message\" role=\"alert\">\n <strong>The contribution could not render.</strong>\n <span>{this.state.error.message}</span>\n </div>\n );\n }\n return this.props.children;\n }\n}\n","import type {\n TapPageProps,\n TapRowActionProps,\n TapSettingsProps,\n TapSettingsValues,\n TapTabProps,\n TapWidgetProps,\n} from '../../client/types';\nimport { uiContributions } from '../../manifest/define';\nimport type {\n ExposableKind,\n ExtensionManifest,\n UiContributionRef,\n} from '../../manifest/types';\nimport { fixtureKeywords } from '../../testing/fixtures';\n\nexport interface PreviewSelection {\n contribution: UiContributionRef;\n label: string;\n}\n\nexport interface PreviewState {\n settings: TapSettingsValues;\n onSettingsChange(values: TapSettingsValues): void;\n}\n\nexport type PreviewProps =\n | TapWidgetProps\n | TapTabProps\n | TapPageProps\n | TapRowActionProps\n | TapSettingsProps;\n\nconst kindLabel: Record<ExposableKind, string> = {\n page: 'page',\n tab: 'tab',\n widget: 'widget',\n rowAction: 'row action',\n settings: 'settings',\n};\n\nfunction titleByExpose(manifest: ExtensionManifest): Map<string, string> {\n const titles = new Map<string, string>();\n const contributes = manifest.contributes;\n\n for (const page of contributes.pages ?? []) {\n titles.set(`./pages/${page.id}`, page.title);\n }\n for (const tab of contributes.tabs ?? []) {\n titles.set(`./tabs/${tab.id}`, tab.title);\n }\n for (const widget of contributes.widgets ?? []) {\n titles.set(`./widgets/${widget.id}`, widget.title);\n }\n for (const action of contributes.rowActions ?? []) {\n titles.set(`./rowActions/${action.id}`, action.title);\n }\n if (contributes.settings) titles.set('./settings', 'Settings');\n\n return titles;\n}\n\nexport function previewSelections(\n manifest: ExtensionManifest,\n): PreviewSelection[] {\n const titles = titleByExpose(manifest);\n return uiContributions(manifest).map(contribution => {\n const title = titles.get(contribution.expose) ?? contribution.id;\n return {\n contribution,\n label:\n contribution.kind === 'settings'\n ? title\n : `${title} (${kindLabel[contribution.kind]})`,\n };\n });\n}\n\nexport function previewProps(\n contribution: UiContributionRef,\n state: PreviewState,\n): PreviewProps {\n switch (contribution.kind) {\n case 'page':\n return { params: { pageId: contribution.id, path: '/' } };\n case 'tab':\n return {};\n case 'widget':\n return { config: {} };\n case 'rowAction':\n return { row: { ...fixtureKeywords[0] } };\n case 'settings':\n return {\n values: state.settings,\n onChange: values => state.onSettingsChange(values),\n };\n }\n}\n\nexport function previewTopology(manifest: ExtensionManifest): string {\n return uiContributions(manifest)\n .map(({ kind, id, expose, entry }) =>\n JSON.stringify({ kind, id, expose, entry }),\n )\n .sort()\n .join('\\n');\n}\n","export const previewStyles = `\n:root {\n color-scheme: light;\n font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n background: #f2f3ee;\n color: #121212;\n font-synthesis: none;\n text-rendering: optimizeLegibility;\n}\n* { box-sizing: border-box; }\nbody { margin: 0; min-width: 320px; background: #f2f3ee; }\nbutton, select { font: inherit; }\n.tap-preview {\n --preview-canvas: #f2f3ee;\n --preview-paper: #fdfdfd;\n --preview-ink: #121212;\n --preview-muted: #686b66;\n --preview-border: #d5d7d1;\n --preview-control: #f7f8f4;\n --preview-green: #235027;\n min-height: 100vh;\n display: grid;\n grid-template-rows: auto 1fr;\n background: var(--preview-canvas);\n color: var(--preview-ink);\n}\n.tap-preview[data-theme=\"dark\"] {\n color-scheme: dark;\n --preview-canvas: #111216;\n --preview-paper: #181920;\n --preview-ink: #f4f5f1;\n --preview-muted: #a7aaa4;\n --preview-border: #303239;\n --preview-control: #202229;\n --preview-green: #b4d773;\n}\n.tap-preview-bar {\n min-height: 68px;\n display: flex;\n gap: 24px;\n align-items: center;\n padding: 12px 20px;\n border-bottom: 1px solid var(--preview-border);\n background: var(--preview-paper);\n}\n.tap-preview-brand { min-width: 210px; margin-right: auto; display: flex; align-items: center; gap: 10px; }\n.tap-preview-brand img { width: 36px; height: 36px; flex: 0 0 auto; object-fit: cover; border: 1px solid var(--preview-border); border-radius: 8px; background: #fff; }\n.tap-preview-brand strong, .tap-preview-brand span { display: block; }\n.tap-preview-brand strong { font-size: 14px; font-weight: 650; letter-spacing: -0.01em; }\n.tap-preview-brand span { margin-top: 2px; color: var(--preview-muted); font-size: 11px; }\n.tap-preview-brand i { width: 6px; height: 6px; display: inline-block; margin-right: 6px; border-radius: 50%; background: #64a75f; box-shadow: 0 0 0 2px rgb(100 167 95 / 14%); }\n.tap-preview-controls { display: flex; align-items: end; gap: 8px; }\n.tap-preview-field { min-width: 104px; display: grid; gap: 4px; color: var(--preview-muted); font-size: 10px; font-weight: 600; letter-spacing: 0.045em; text-transform: uppercase; }\n.tap-preview-field--contribution { min-width: 190px; }\n.tap-preview-field select, .tap-preview-reset {\n height: 32px;\n border: 1px solid var(--preview-border);\n border-radius: 6px;\n outline: none;\n background: var(--preview-control);\n color: var(--preview-ink);\n font-size: 12px;\n font-weight: 500;\n letter-spacing: normal;\n text-transform: none;\n}\n.tap-preview-field select { width: 100%; padding: 0 28px 0 9px; cursor: pointer; }\n.tap-preview-field select:focus, .tap-preview-reset:focus-visible { border-color: var(--preview-green); box-shadow: 0 0 0 2px color-mix(in srgb, var(--preview-green) 18%, transparent); }\n.tap-preview-reset { padding: 0 12px; cursor: pointer; }\n.tap-preview-reset:hover { border-color: color-mix(in srgb, var(--preview-ink) 34%, var(--preview-border)); background: var(--preview-paper); }\n.tap-preview-stage { min-width: 0; padding: 22px 24px 14px; overflow: auto; }\n.tap-preview-canvas {\n width: 100%;\n max-width: 1440px;\n height: max(380px, calc(100vh - 118px));\n margin: 0 auto;\n border: 1px solid var(--preview-border);\n border-radius: 8px;\n overflow: hidden;\n background: var(--preview-paper);\n color: var(--preview-ink);\n box-shadow: 0 2px 8px rgb(20 24 18 / 5%);\n transition: max-width 160ms ease;\n}\n.tap-preview-canvas[data-viewport=\"tablet\"] { max-width: 768px; }\n.tap-preview-canvas[data-viewport=\"mobile\"] { max-width: 390px; }\n.tap-preview-canvas[data-size=\"slot\"] { width: min(100%, 680px); height: 380px; }\n.tap-preview-canvas[data-size=\"panel\"] { width: min(100%, 960px); height: clamp(420px, calc(100vh - 118px), 620px); }\n.tap-preview-canvas[data-size=\"page\"] { height: max(380px, calc(100vh - 118px)); }\n.tap-preview-surface { display: block; height: 100%; min-height: 0; }\n.tap-preview-message { min-height: 360px; display: grid; place-content: center; gap: 8px; padding: 32px; text-align: center; }\n.tap-preview-message span { max-width: 62ch; color: var(--preview-muted); }\n@media (max-width: 640px) {\n .tap-preview-stage { padding: 12px; }\n .tap-preview-bar { align-items: flex-start; padding: 12px; }\n .tap-preview-brand { min-width: 0; }\n .tap-preview-controls { width: 100%; overflow-x: auto; padding-bottom: 2px; }\n .tap-preview-field { min-width: 100px; }\n .tap-preview-field--contribution { min-width: 180px; }\n}\n@media (max-width: 900px) {\n .tap-preview-bar { flex-wrap: wrap; gap: 12px; }\n .tap-preview-controls { flex: 1 1 100%; }\n}\n`;\n"],"mappings":"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","names":["jsx","jsxs","Component","jsx"]}
package/dist/vite.cjs CHANGED
@@ -20,17 +20,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/vite.ts
21
21
  var vite_exports = {};
22
22
  __export(vite_exports, {
23
+ PREVIEW_REMOTE_ENV: () => PREVIEW_REMOTE_ENV,
23
24
  VIRTUAL_PREFIX: () => VIRTUAL_PREFIX,
24
25
  collectEntryStyles: () => collectEntryStyles,
25
26
  inlineCssImports: () => inlineCssImports,
27
+ previewRemote: () => previewRemote,
26
28
  tappifyExtension: () => tappifyExtension
27
29
  });
28
30
  module.exports = __toCommonJS(vite_exports);
29
31
 
30
32
  // src/vite/plugin.ts
31
- var import_vite = require("@module-federation/vite");
33
+ var import_vite2 = require("@module-federation/vite");
32
34
  var import_node_fs2 = require("fs");
33
- var import_node_path2 = require("path");
35
+ var import_node_path3 = require("path");
34
36
 
35
37
  // src/client/errors.ts
36
38
  var TAP_ERROR_CODES = [
@@ -172,6 +174,13 @@ var SCOPES = [
172
174
  requiresSecurityReview: false,
173
175
  requiresJustification: false
174
176
  },
177
+ {
178
+ key: "review.threads:read",
179
+ label: "Read Review threads",
180
+ description: "Review threads, comments, screenshot descriptors, build context and safe device context",
181
+ requiresSecurityReview: false,
182
+ requiresJustification: false
183
+ },
175
184
  {
176
185
  key: "crashes:read",
177
186
  label: "Read crash data",
@@ -245,7 +254,7 @@ var SCOPES = [
245
254
  {
246
255
  key: "work:sync",
247
256
  label: "Sync work items",
248
- description: "Two-way comments and status on created work items",
257
+ description: "Sync comments and status on created work items",
249
258
  requiresSecurityReview: false,
250
259
  requiresJustification: true
251
260
  },
@@ -693,7 +702,7 @@ var aiContribution = import_zod.z.object({
693
702
  ).optional()
694
703
  }).strict();
695
704
  var statusNames = (stage) => import_zod.z.array(import_zod.z.string()).describe(`Your own status names that mean ${stage}.`);
696
- var workContribution = import_zod.z.object({
705
+ var legacyWorkContribution = import_zod.z.object({
697
706
  kind: import_zod.z.enum(["tracker", "docs", "chat"]).describe("What kind of destination this is."),
698
707
  container: import_zod.z.object({
699
708
  label: import_zod.z.string().min(1).max(60).describe(
@@ -728,6 +737,55 @@ var workContribution = import_zod.z.object({
728
737
  "Whether items are created as the owner who asked (`user`) or as the install itself."
729
738
  )
730
739
  }).strict();
740
+ var workDirection = import_zod.z.enum([
741
+ "none",
742
+ "to_provider",
743
+ "from_provider",
744
+ "two-way"
745
+ ]);
746
+ var workAuth = import_zod.z.discriminatedUnion("method", [
747
+ import_zod.z.object({
748
+ method: import_zod.z.literal("oauth"),
749
+ provider: import_zod.z.string().min(1).describe("The OAuth provider identifier."),
750
+ scopes: import_zod.z.array(import_zod.z.string()).describe("The scopes requested from the provider.")
751
+ }).strict(),
752
+ import_zod.z.object({
753
+ method: import_zod.z.literal("api_key"),
754
+ fields: import_zod.z.array(credentialField).max(20)
755
+ }).strict()
756
+ ]);
757
+ var currentWorkContribution = legacyWorkContribution.extend({
758
+ container: legacyWorkContribution.shape.container.describe(
759
+ "Deprecated. Resolve containers dynamically with describe_container."
760
+ ).optional(),
761
+ fields: legacyWorkContribution.shape.fields.describe("Deprecated. Resolve fields dynamically with describe_container.").optional(),
762
+ comments: import_zod.z.union([
763
+ import_zod.z.enum(["two-way", "none"]).describe("Deprecated. Declare a comments capability object."),
764
+ import_zod.z.object({
765
+ direction: workDirection,
766
+ supportsEdits: import_zod.z.literal(false),
767
+ supportsDeletes: import_zod.z.literal(false)
768
+ }).strict()
769
+ ]),
770
+ status: import_zod.z.object({
771
+ direction: workDirection,
772
+ webhook: import_zod.z.string().regex(DOMAIN_EVENT).optional(),
773
+ map: import_zod.z.object({
774
+ open: statusNames("not started"),
775
+ in_progress: statusNames("under way"),
776
+ done: statusNames("finished")
777
+ }).strict().describe(
778
+ "Deprecated. Resolve statuses dynamically with describe_container."
779
+ ).optional()
780
+ }).strict().optional(),
781
+ auth: workAuth.describe("How owners authenticate the work destination.")
782
+ });
783
+ var workContribution = import_zod.z.union([
784
+ currentWorkContribution,
785
+ legacyWorkContribution.describe(
786
+ "Deprecated SDK v2 work declaration. Migrate to work capabilities and auth."
787
+ )
788
+ ]);
731
789
  var listing = import_zod.z.object({
732
790
  longDescription: import_zod.z.string().min(1).max(5e3).describe("The body of your listing page, in markdown.").optional(),
733
791
  screenshots: import_zod.z.array(import_zod.z.string().min(1)).min(1, "A listing shows 1 to 6 screenshots.").max(6, "A listing shows 1 to 6 screenshots.").describe(
@@ -819,7 +877,7 @@ var manifestObject = import_zod.z.object({
819
877
  "What this extension offers the assistant: tools, mentions, actions, knowledge, skills, prompts and context providers."
820
878
  ).optional(),
821
879
  work: workContribution.describe(
822
- "The work destination this extension creates issues, pages or messages in. Needs the `work:create` and `work:sync` scopes."
880
+ "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`."
823
881
  ).optional()
824
882
  }).strict().describe(
825
883
  "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."
@@ -1040,16 +1098,36 @@ var manifestSchema = manifestObject.superRefine((manifest, ctx) => {
1040
1098
  }
1041
1099
  }
1042
1100
  if (contributes.work) {
1101
+ const work = contributes.work;
1102
+ const legacy = isLegacyWork(work);
1103
+ if (!legacy || !granted.has("reviews:read")) {
1104
+ requireScope(
1105
+ "review.threads:read",
1106
+ ["scopes"],
1107
+ "the work destination reads Review threads."
1108
+ );
1109
+ }
1043
1110
  requireScope(
1044
1111
  "work:create",
1045
1112
  ["scopes"],
1046
1113
  "the work destination creates items."
1047
1114
  );
1048
- requireScope(
1049
- "work:sync",
1050
- ["scopes"],
1051
- "the work destination syncs comments and status."
1052
- );
1115
+ const comments = typeof work.comments === "string" ? work.comments : work.comments.direction;
1116
+ const status = work.status ? "direction" in work.status ? work.status.direction : "from_provider" : "none";
1117
+ if (comments !== "none" || status !== "none") {
1118
+ requireScope(
1119
+ "work:sync",
1120
+ ["scopes"],
1121
+ "the work destination syncs comments or status."
1122
+ );
1123
+ }
1124
+ if (work.identity === "user") {
1125
+ ctx.addIssue({
1126
+ code: "custom",
1127
+ path: ["contributes", "work", "identity"],
1128
+ message: 'Per-user work identity is not supported. Set identity to "install".'
1129
+ });
1130
+ }
1053
1131
  }
1054
1132
  const needsServer = contributes.connector !== void 0 || (contributes.webhooks?.length ?? 0) > 0 || ai !== void 0 || contributes.work !== void 0 || manifest.server?.procedures !== void 0;
1055
1133
  if (needsServer && !manifest.server?.baseUrl) {
@@ -1124,10 +1202,176 @@ function validateManifest(value) {
1124
1202
  }));
1125
1203
  return { ok: false, issues };
1126
1204
  }
1205
+ function isLegacyWork(work) {
1206
+ return !("auth" in work);
1207
+ }
1208
+
1209
+ // src/vite/preview/plugin.ts
1210
+ var import_node_crypto = require("crypto");
1211
+ var import_node_path = require("path");
1212
+ var import_vite = require("vite");
1213
+ var PREVIEW_CLIENT_ID = "virtual:tappify-preview/client";
1214
+ var PREVIEW_MANIFEST_ID = "virtual:tappify-preview/manifest";
1215
+ var PREVIEW_REMOTE_ID = "virtual:tappify-preview/remote";
1216
+ var PREVIEW_REMOTE_ENV = "TAPPIFY_PREVIEW_REMOTE";
1217
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
1218
+ var VIRTUAL_IDS = /* @__PURE__ */ new Set([
1219
+ PREVIEW_CLIENT_ID,
1220
+ PREVIEW_MANIFEST_ID,
1221
+ PREVIEW_REMOTE_ID
1222
+ ]);
1223
+ function previewRemote(remote, port) {
1224
+ if (remote === void 0 || remote.trim() === "") {
1225
+ return { ok: true, url: `http://localhost:${String(port)}/remoteEntry.js` };
1226
+ }
1227
+ let parsed;
1228
+ try {
1229
+ parsed = new URL(remote);
1230
+ } catch {
1231
+ return { ok: false, message: "The preview remote is not a valid URL." };
1232
+ }
1233
+ if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && LOOPBACK_HOSTS.has(parsed.hostname))) {
1234
+ return {
1235
+ ok: false,
1236
+ message: "A preview remote must use HTTPS or loopback HTTP."
1237
+ };
1238
+ }
1239
+ return { ok: true, url: parsed.toString() };
1240
+ }
1241
+ var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1242
+ function messageDocument(title2, messages) {
1243
+ return `<!doctype html>
1244
+ <html lang="en">
1245
+ <head>
1246
+ <meta charset="UTF-8" />
1247
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1248
+ <title>${escapeHtml(title2)} \xB7 Tappify local preview</title>
1249
+ <style>body{margin:0;min-height:100vh;display:grid;place-content:center;padding:32px;box-sizing:border-box;background:#f4f5ef;color:#142012;font-family:Inter,ui-sans-serif,system-ui,sans-serif}main{max-width:720px;padding:28px;border:1px solid #d8ddcf;border-radius:12px;background:#fff;box-shadow:0 12px 36px rgb(20 32 18 / 9%)}h1{font-size:22px;margin:0 0 14px}ul{margin:0;padding-left:22px;color:#4d5849}li+li{margin-top:8px}</style>
1250
+ </head>
1251
+ <body><main><h1>${escapeHtml(title2)}</h1><ul>${messages.map((message) => `<li>${escapeHtml(message)}</li>`).join("")}</ul></main></body>
1252
+ </html>`;
1253
+ }
1254
+ function previewDocument(manifest, remote) {
1255
+ if (!manifest.ok) {
1256
+ return messageDocument(
1257
+ "Manifest is invalid",
1258
+ manifest.issues.map((issue) => `${issue.path}: ${issue.message}`)
1259
+ );
1260
+ }
1261
+ if (!remote.ok)
1262
+ return messageDocument("Preview remote was refused", [remote.message]);
1263
+ const icon = manifest.manifest.icon.replace(/^\.\//, "");
1264
+ return `<!doctype html>
1265
+ <html lang="en">
1266
+ <head>
1267
+ <meta charset="UTF-8" />
1268
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1269
+ <link rel="icon" href="/${escapeHtml(icon)}" />
1270
+ <title>${escapeHtml(manifest.manifest.name)} \xB7 Tappify local preview</title>
1271
+ </head>
1272
+ <body>
1273
+ <div id="tappify-preview-root"></div>
1274
+ <script type="module" src="/@id/${PREVIEW_CLIENT_ID}"></script>
1275
+ </body>
1276
+ </html>`;
1277
+ }
1278
+ var moduleSource = (value) => `export default ${JSON.stringify(value)};`;
1279
+ function optimizedReactUrl(root, cacheDir, cacheGeneration) {
1280
+ const depsCacheDir = (0, import_vite.normalizePath)((0, import_node_path.resolve)(cacheDir, "deps"));
1281
+ const relativeCacheDir = (0, import_vite.normalizePath)((0, import_node_path.relative)(root, depsCacheDir));
1282
+ const prefix = (0, import_node_path.isAbsolute)(relativeCacheDir) || relativeCacheDir.startsWith("../") ? `/@fs/${depsCacheDir.replace(/^\/+/, "")}` : `/${relativeCacheDir}`;
1283
+ return `${prefix}/react.js?tappify=${encodeURIComponent(cacheGeneration)}`;
1284
+ }
1285
+ function createPreviewPlugins(options) {
1286
+ const remote = previewRemote(options.remote, options.port);
1287
+ const cacheGeneration = (0, import_node_crypto.randomUUID)();
1288
+ let reactUrl = optimizedReactUrl(
1289
+ (0, import_node_path.resolve)(options.manifestFile, ".."),
1290
+ (0, import_node_path.resolve)(options.manifestFile, "../node_modules/.vite"),
1291
+ cacheGeneration
1292
+ );
1293
+ const preview = {
1294
+ name: "tappify:preview",
1295
+ apply: "serve",
1296
+ config() {
1297
+ return {
1298
+ optimizeDeps: {
1299
+ exclude: ["@tappify/extension-sdk/vite/preview/bootstrap"]
1300
+ }
1301
+ };
1302
+ },
1303
+ configResolved(config) {
1304
+ reactUrl = optimizedReactUrl(
1305
+ config.root,
1306
+ config.cacheDir,
1307
+ cacheGeneration
1308
+ );
1309
+ },
1310
+ resolveId(id) {
1311
+ if (VIRTUAL_IDS.has(id)) return `\0${id}`;
1312
+ if (id.startsWith("\0") && VIRTUAL_IDS.has(id.slice(1))) return id;
1313
+ return null;
1314
+ },
1315
+ load(id) {
1316
+ if (id === `\0${PREVIEW_MANIFEST_ID}`) {
1317
+ return moduleSource(options.loadManifest());
1318
+ }
1319
+ if (id === `\0${PREVIEW_REMOTE_ID}`) return moduleSource(remote);
1320
+ if (id !== `\0${PREVIEW_CLIENT_ID}`) return null;
1321
+ return [
1322
+ `import { startLocalPreview } from '@tappify/extension-sdk/vite/preview/bootstrap';`,
1323
+ `import manifest from '${PREVIEW_MANIFEST_ID}';`,
1324
+ `import remote from '${PREVIEW_REMOTE_ID}';`,
1325
+ `const mount = document.getElementById('tappify-preview-root');`,
1326
+ `if (mount && manifest.ok && remote.ok) void startLocalPreview({ manifest: manifest.manifest, remote: remote.url, reactUrl: ${JSON.stringify(reactUrl)}, mount });`
1327
+ ].join("\n");
1328
+ },
1329
+ configureServer(server) {
1330
+ server.middlewares.use((request, response, next) => {
1331
+ const path = request.url?.split("?", 1)[0];
1332
+ if (path !== "/" && path !== "/index.html") return next();
1333
+ void server.transformIndexHtml(
1334
+ path,
1335
+ previewDocument(options.loadManifest(), remote)
1336
+ ).then((html) => {
1337
+ response.statusCode = 200;
1338
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
1339
+ response.end(html);
1340
+ }).catch(next);
1341
+ });
1342
+ }
1343
+ };
1344
+ const dev = {
1345
+ name: "tappify:dev",
1346
+ apply: "serve",
1347
+ config() {
1348
+ return {
1349
+ server: {
1350
+ host: "127.0.0.1",
1351
+ port: options.port,
1352
+ strictPort: true,
1353
+ cors: { origin: true },
1354
+ origin: `http://localhost:${String(options.port)}`
1355
+ }
1356
+ };
1357
+ },
1358
+ configureServer(server) {
1359
+ server.watcher.add(options.manifestFile);
1360
+ },
1361
+ async hotUpdate(context) {
1362
+ if (this.environment.name !== "client") return;
1363
+ if (context.file !== options.manifestFile) return;
1364
+ const manifest = options.loadManifest();
1365
+ await options.onManifestChange?.(manifest);
1366
+ context.server.ws.send({ type: "full-reload", path: "*" });
1367
+ }
1368
+ };
1369
+ return [preview, dev];
1370
+ }
1127
1371
 
1128
1372
  // src/vite/styles.ts
1129
1373
  var import_node_fs = require("fs");
1130
- var import_node_path = require("path");
1374
+ var import_node_path2 = require("path");
1131
1375
  var IMPORT_PATTERN = /(?:^|\n)\s*(?:import|export)(?:[^'"]*?from\s*)?\s*['"]([^'"]+)['"]/g;
1132
1376
  var IMPORT_SPECIFIER_PATTERN = /((?:^|\n)\s*(?:import|export)(?:[^'"]*?from\s*)?\s*['"])([^'"]+)(['"])/g;
1133
1377
  var BLOCK_COMMENT_PATTERN = /\/\*[\s\S]*?\*\//g;
@@ -1140,20 +1384,20 @@ function isPlainStylesheet(specifier) {
1140
1384
  return specifier.endsWith(".css") && !specifier.endsWith(".module.css");
1141
1385
  }
1142
1386
  function resolveSource(specifier, importer) {
1143
- const base = (0, import_node_path.resolve)((0, import_node_path.dirname)(importer), specifier);
1144
- if ((0, import_node_path.extname)(base) !== "" && (0, import_node_fs.existsSync)(base)) return base;
1387
+ const base = (0, import_node_path2.resolve)((0, import_node_path2.dirname)(importer), specifier);
1388
+ if ((0, import_node_path2.extname)(base) !== "" && (0, import_node_fs.existsSync)(base)) return base;
1145
1389
  for (const extension of SOURCE_EXTENSIONS) {
1146
1390
  const candidate = `${base}${extension}`;
1147
1391
  if ((0, import_node_fs.existsSync)(candidate)) return candidate;
1148
1392
  }
1149
1393
  for (const extension of SOURCE_EXTENSIONS) {
1150
- const candidate = (0, import_node_path.resolve)(base, `index${extension}`);
1394
+ const candidate = (0, import_node_path2.resolve)(base, `index${extension}`);
1151
1395
  if ((0, import_node_fs.existsSync)(candidate)) return candidate;
1152
1396
  }
1153
1397
  return null;
1154
1398
  }
1155
1399
  function collectEntryStyles(entryAbsolutePath, root) {
1156
- const boundary = root.endsWith(import_node_path.sep) ? root : `${root}${import_node_path.sep}`;
1400
+ const boundary = root.endsWith(import_node_path2.sep) ? root : `${root}${import_node_path2.sep}`;
1157
1401
  const styles = [];
1158
1402
  const seenStyles = /* @__PURE__ */ new Set();
1159
1403
  const seenFiles = /* @__PURE__ */ new Set();
@@ -1169,7 +1413,7 @@ function collectEntryStyles(entryAbsolutePath, root) {
1169
1413
  while (match !== null) {
1170
1414
  const specifier = match[1];
1171
1415
  if (specifier.endsWith(".css")) {
1172
- const value = specifier.startsWith(".") ? (0, import_node_path.resolve)((0, import_node_path.dirname)(file), specifier) : specifier;
1416
+ const value = specifier.startsWith(".") ? (0, import_node_path2.resolve)((0, import_node_path2.dirname)(file), specifier) : specifier;
1173
1417
  if (!seenStyles.has(value)) {
1174
1418
  seenStyles.add(value);
1175
1419
  styles.push(value);
@@ -1202,38 +1446,52 @@ var DEFAULT_PORT = 5273;
1202
1446
  var SOURCE_FILE = /\.(?:[cm]?[jt]sx?)$/;
1203
1447
  var federationOptionsByPlugins = /* @__PURE__ */ new WeakMap();
1204
1448
  function manifestFile(options) {
1205
- return options.manifestPath ?? (0, import_node_path2.resolve)(options.root ?? process.cwd(), "tappify.extension.json");
1449
+ return options.manifestPath ?? (0, import_node_path3.resolve)(options.root ?? process.cwd(), "tappify.extension.json");
1206
1450
  }
1207
1451
  function loadManifest(options) {
1208
- const raw = options.manifest ?? JSON.parse((0, import_node_fs2.readFileSync)(manifestFile(options), "utf8"));
1209
- const result = validateManifest(raw);
1210
- if (!result.ok) {
1211
- const lines = result.issues.map((issue) => ` ${issue.path}: ${issue.message}`).join("\n");
1212
- throw new TapError(
1213
- "TAP_MANIFEST_INVALID",
1214
- `tappify.extension.json does not validate:
1452
+ let raw;
1453
+ try {
1454
+ raw = options.manifest ?? JSON.parse((0, import_node_fs2.readFileSync)(manifestFile(options), "utf8"));
1455
+ } catch (error) {
1456
+ return {
1457
+ ok: false,
1458
+ issues: [
1459
+ {
1460
+ path: "$",
1461
+ message: error instanceof Error ? `The manifest is not valid JSON: ${error.message}` : "The manifest is not valid JSON."
1462
+ }
1463
+ ]
1464
+ };
1465
+ }
1466
+ return validateManifest(raw);
1467
+ }
1468
+ function manifestError(result) {
1469
+ if (result.ok) throw new Error("A valid manifest has no validation error.");
1470
+ const lines = result.issues.map((issue) => ` ${issue.path}: ${issue.message}`).join("\n");
1471
+ return new TapError(
1472
+ "TAP_MANIFEST_INVALID",
1473
+ `tappify.extension.json does not validate:
1215
1474
  ${lines}
1216
1475
  Fix the fields above, or run \`tappify extension doctor\` for the same list with suggested fixes.`
1217
- );
1218
- }
1219
- return result.manifest;
1476
+ );
1220
1477
  }
1221
1478
  function tappifyExtension(options = {}) {
1222
1479
  const root = options.root ?? process.cwd();
1223
- const boundary = root.endsWith(import_node_path2.sep) ? root : `${root}${import_node_path2.sep}`;
1224
- const manifest = loadManifest(options);
1225
- const contributions = uiContributions(manifest);
1480
+ const boundary = root.endsWith(import_node_path3.sep) ? root : `${root}${import_node_path3.sep}`;
1481
+ const loaded = loadManifest(options);
1482
+ const manifest = loaded.ok ? loaded.manifest : null;
1483
+ const contributions = manifest === null ? [] : uiContributions(manifest);
1226
1484
  const port = options.port ?? DEFAULT_PORT;
1227
1485
  const entryByKey = /* @__PURE__ */ new Map();
1228
1486
  const exposes = {};
1229
1487
  for (const contribution of contributions) {
1230
1488
  const key = contribution.expose.slice("./".length);
1231
- entryByKey.set(key, (0, import_node_path2.resolve)(root, contribution.entry));
1489
+ entryByKey.set(key, (0, import_node_path3.resolve)(root, contribution.entry));
1232
1490
  exposes[contribution.expose] = `${VIRTUAL_PREFIX}${key}`;
1233
1491
  }
1234
1492
  const singleton = { singleton: true, requiredVersion: false };
1235
1493
  const federationOptions = {
1236
- name: remoteName(manifest.id),
1494
+ name: remoteName(manifest?.id ?? "invalid-extension"),
1237
1495
  filename: "remoteEntry.js",
1238
1496
  manifest: true,
1239
1497
  exposes,
@@ -1289,56 +1547,55 @@ function tappifyExtension(options = {}) {
1289
1547
  ].join("\n");
1290
1548
  }
1291
1549
  };
1550
+ const guard = {
1551
+ name: "tappify:manifest",
1552
+ enforce: "pre",
1553
+ config(_config, environment) {
1554
+ if (environment.command !== "build") return null;
1555
+ const current = loadManifest(options);
1556
+ if (!current.ok) throw manifestError(current);
1557
+ return null;
1558
+ }
1559
+ };
1292
1560
  const css = {
1293
1561
  name: "tappify:css",
1294
1562
  enforce: "pre",
1295
1563
  transform(code, id) {
1296
1564
  if (!id.startsWith(boundary)) return null;
1297
- if (id.includes(`${import_node_path2.sep}node_modules${import_node_path2.sep}`)) return null;
1565
+ if (id.includes(`${import_node_path3.sep}node_modules${import_node_path3.sep}`)) return null;
1298
1566
  const file = id.split("?")[0];
1299
1567
  if (!SOURCE_FILE.test(file)) return null;
1300
1568
  const next = inlineCssImports(code);
1301
1569
  return next === null ? null : { code: next, map: null };
1302
1570
  }
1303
1571
  };
1304
- const dev = {
1305
- name: "tappify:dev",
1306
- apply: "serve",
1307
- config() {
1308
- return {
1309
- server: {
1310
- port,
1311
- strictPort: true,
1312
- cors: { origin: true },
1313
- origin: `http://localhost:${String(port)}`
1314
- }
1315
- };
1316
- },
1317
- configureServer(server) {
1318
- server.watcher.add(manifestFile(options));
1319
- },
1320
- /** Vite runs hotUpdate once per dev environment, and the manifest is one file. */
1321
- async hotUpdate(context) {
1322
- if (this.environment.name !== "client") return;
1323
- if (context.file !== manifestFile(options)) return;
1324
- if (options.onManifestChange === void 0) return;
1325
- await options.onManifestChange(loadManifest(options));
1326
- }
1327
- };
1328
1572
  const plugins = [
1573
+ guard,
1329
1574
  expose,
1330
1575
  css,
1331
- ...(0, import_vite.federation)(federationOptions),
1332
- dev
1576
+ ...(0, import_vite2.federation)(federationOptions),
1577
+ ...createPreviewPlugins({
1578
+ manifestFile: manifestFile(options),
1579
+ loadManifest: () => loadManifest(options),
1580
+ port,
1581
+ remote: process.env[PREVIEW_REMOTE_ENV],
1582
+ onManifestChange: async (result) => {
1583
+ if (result.ok && options.onManifestChange !== void 0) {
1584
+ await options.onManifestChange(result.manifest);
1585
+ }
1586
+ }
1587
+ })
1333
1588
  ];
1334
1589
  federationOptionsByPlugins.set(plugins, federationOptions);
1335
1590
  return plugins;
1336
1591
  }
1337
1592
  // Annotate the CommonJS export names for ESM import in node:
1338
1593
  0 && (module.exports = {
1594
+ PREVIEW_REMOTE_ENV,
1339
1595
  VIRTUAL_PREFIX,
1340
1596
  collectEntryStyles,
1341
1597
  inlineCssImports,
1598
+ previewRemote,
1342
1599
  tappifyExtension
1343
1600
  });
1344
1601
  //# sourceMappingURL=vite.cjs.map