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

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-BO3kavn0.d.ts +25 -0
  51. package/dist/client-DE-vMgdO.d.cts +25 -0
  52. package/dist/codegen.cjs.map +1 -1
  53. package/dist/codegen.d.cts +43 -7
  54. package/dist/codegen.d.ts +43 -7
  55. package/dist/codegen.mjs.map +1 -1
  56. package/dist/{constants-DJ_KgZpN.d.cts → constants-Bh759FUI.d.cts} +44 -8
  57. package/dist/{constants-DJ_KgZpN.d.ts → constants-Bh759FUI.d.ts} +44 -8
  58. package/dist/{context-Dt6xh_5t.d.cts → context-DI0EJCRD.d.cts} +5 -4
  59. package/dist/{context-Ml7k_V14.d.ts → context-mr8R5CNt.d.ts} +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 +142 -86
  75. package/dist/manifest.cjs.map +1 -1
  76. package/dist/manifest.d.cts +97 -12
  77. package/dist/manifest.d.ts +97 -12
  78. package/dist/manifest.mjs +129 -73
  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-DDAGLpov.d.ts → types-BdDaLnqs.d.cts} +4 -3
  103. package/dist/{types-DrpQSv_2.d.cts → types-uGHrRaOj.d.ts} +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 +320 -60
  124. package/dist/vite.cjs.map +1 -1
  125. package/dist/vite.d.cts +54 -8
  126. package/dist/vite.d.ts +54 -8
  127. package/dist/vite.mjs +312 -54
  128. package/dist/vite.mjs.map +1 -1
  129. package/dist/work-events-BlOVpK0k.d.ts +547 -0
  130. package/dist/work-events-D97EoqvS.d.cts +547 -0
  131. package/package.json +19 -2
  132. package/schema/extension-v2.json +440 -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.2\",\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
  },
@@ -549,7 +558,10 @@ var connectorContribution = import_zod.z.object({
549
558
  ),
550
559
  scopes: import_zod.z.array(import_zod.z.string()).describe(
551
560
  "The scopes asked of the outside service. These are that service's scopes, not Tappify's."
552
- )
561
+ ),
562
+ tokenRequestEncoding: import_zod.z.enum(["form", "json"]).describe(
563
+ "The request body encoding for authorization-code and refresh token exchanges. When omitted, uses form encoding (application/x-www-form-urlencoded); json uses application/json."
564
+ ).optional()
553
565
  }).strict().describe('The endpoints, for `method: "oauth"`.').optional()
554
566
  }).strict().describe("How owners authenticate this connector.").optional(),
555
567
  sync: import_zod.z.enum(["5m", "15m", "1h", "6h", "24h"]).describe("How often Tappify asks your server to sync.").optional(),
@@ -693,7 +705,7 @@ var aiContribution = import_zod.z.object({
693
705
  ).optional()
694
706
  }).strict();
695
707
  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({
708
+ var legacyWorkContribution = import_zod.z.object({
697
709
  kind: import_zod.z.enum(["tracker", "docs", "chat"]).describe("What kind of destination this is."),
698
710
  container: import_zod.z.object({
699
711
  label: import_zod.z.string().min(1).max(60).describe(
@@ -728,6 +740,55 @@ var workContribution = import_zod.z.object({
728
740
  "Whether items are created as the owner who asked (`user`) or as the install itself."
729
741
  )
730
742
  }).strict();
743
+ var workDirection = import_zod.z.enum([
744
+ "none",
745
+ "to_provider",
746
+ "from_provider",
747
+ "two-way"
748
+ ]);
749
+ var workAuth = import_zod.z.discriminatedUnion("method", [
750
+ import_zod.z.object({
751
+ method: import_zod.z.literal("oauth"),
752
+ provider: import_zod.z.string().min(1).describe("The OAuth provider identifier."),
753
+ scopes: import_zod.z.array(import_zod.z.string()).describe("The scopes requested from the provider.")
754
+ }).strict(),
755
+ import_zod.z.object({
756
+ method: import_zod.z.literal("api_key"),
757
+ fields: import_zod.z.array(credentialField).max(20)
758
+ }).strict()
759
+ ]);
760
+ var currentWorkContribution = legacyWorkContribution.extend({
761
+ container: legacyWorkContribution.shape.container.describe(
762
+ "Deprecated. Resolve containers dynamically with describe_container."
763
+ ).optional(),
764
+ fields: legacyWorkContribution.shape.fields.describe("Deprecated. Resolve fields dynamically with describe_container.").optional(),
765
+ comments: import_zod.z.union([
766
+ import_zod.z.enum(["two-way", "none"]).describe("Deprecated. Declare a comments capability object."),
767
+ import_zod.z.object({
768
+ direction: workDirection,
769
+ supportsEdits: import_zod.z.literal(false),
770
+ supportsDeletes: import_zod.z.literal(false)
771
+ }).strict()
772
+ ]),
773
+ status: import_zod.z.object({
774
+ direction: workDirection,
775
+ webhook: import_zod.z.string().regex(DOMAIN_EVENT).optional(),
776
+ map: import_zod.z.object({
777
+ open: statusNames("not started"),
778
+ in_progress: statusNames("under way"),
779
+ done: statusNames("finished")
780
+ }).strict().describe(
781
+ "Deprecated. Resolve statuses dynamically with describe_container."
782
+ ).optional()
783
+ }).strict().optional(),
784
+ auth: workAuth.describe("How owners authenticate the work destination.")
785
+ });
786
+ var workContribution = import_zod.z.union([
787
+ currentWorkContribution,
788
+ legacyWorkContribution.describe(
789
+ "Deprecated SDK v2 work declaration. Migrate to work capabilities and auth."
790
+ )
791
+ ]);
731
792
  var listing = import_zod.z.object({
732
793
  longDescription: import_zod.z.string().min(1).max(5e3).describe("The body of your listing page, in markdown.").optional(),
733
794
  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 +880,7 @@ var manifestObject = import_zod.z.object({
819
880
  "What this extension offers the assistant: tools, mentions, actions, knowledge, skills, prompts and context providers."
820
881
  ).optional(),
821
882
  work: workContribution.describe(
822
- "The work destination this extension creates issues, pages or messages in. Needs the `work:create` and `work:sync` scopes."
883
+ "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
884
  ).optional()
824
885
  }).strict().describe(
825
886
  "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 +1101,36 @@ var manifestSchema = manifestObject.superRefine((manifest, ctx) => {
1040
1101
  }
1041
1102
  }
1042
1103
  if (contributes.work) {
1104
+ const work = contributes.work;
1105
+ const legacy = isLegacyWork(work);
1106
+ if (!legacy || !granted.has("reviews:read")) {
1107
+ requireScope(
1108
+ "review.threads:read",
1109
+ ["scopes"],
1110
+ "the work destination reads Review threads."
1111
+ );
1112
+ }
1043
1113
  requireScope(
1044
1114
  "work:create",
1045
1115
  ["scopes"],
1046
1116
  "the work destination creates items."
1047
1117
  );
1048
- requireScope(
1049
- "work:sync",
1050
- ["scopes"],
1051
- "the work destination syncs comments and status."
1052
- );
1118
+ const comments = typeof work.comments === "string" ? work.comments : work.comments.direction;
1119
+ const status = work.status ? "direction" in work.status ? work.status.direction : "from_provider" : "none";
1120
+ if (comments !== "none" || status !== "none") {
1121
+ requireScope(
1122
+ "work:sync",
1123
+ ["scopes"],
1124
+ "the work destination syncs comments or status."
1125
+ );
1126
+ }
1127
+ if (work.identity === "user") {
1128
+ ctx.addIssue({
1129
+ code: "custom",
1130
+ path: ["contributes", "work", "identity"],
1131
+ message: 'Per-user work identity is not supported. Set identity to "install".'
1132
+ });
1133
+ }
1053
1134
  }
1054
1135
  const needsServer = contributes.connector !== void 0 || (contributes.webhooks?.length ?? 0) > 0 || ai !== void 0 || contributes.work !== void 0 || manifest.server?.procedures !== void 0;
1055
1136
  if (needsServer && !manifest.server?.baseUrl) {
@@ -1124,10 +1205,176 @@ function validateManifest(value) {
1124
1205
  }));
1125
1206
  return { ok: false, issues };
1126
1207
  }
1208
+ function isLegacyWork(work) {
1209
+ return !("auth" in work);
1210
+ }
1211
+
1212
+ // src/vite/preview/plugin.ts
1213
+ var import_node_crypto = require("crypto");
1214
+ var import_node_path = require("path");
1215
+ var import_vite = require("vite");
1216
+ var PREVIEW_CLIENT_ID = "virtual:tappify-preview/client";
1217
+ var PREVIEW_MANIFEST_ID = "virtual:tappify-preview/manifest";
1218
+ var PREVIEW_REMOTE_ID = "virtual:tappify-preview/remote";
1219
+ var PREVIEW_REMOTE_ENV = "TAPPIFY_PREVIEW_REMOTE";
1220
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
1221
+ var VIRTUAL_IDS = /* @__PURE__ */ new Set([
1222
+ PREVIEW_CLIENT_ID,
1223
+ PREVIEW_MANIFEST_ID,
1224
+ PREVIEW_REMOTE_ID
1225
+ ]);
1226
+ function previewRemote(remote, port) {
1227
+ if (remote === void 0 || remote.trim() === "") {
1228
+ return { ok: true, url: `http://localhost:${String(port)}/remoteEntry.js` };
1229
+ }
1230
+ let parsed;
1231
+ try {
1232
+ parsed = new URL(remote);
1233
+ } catch {
1234
+ return { ok: false, message: "The preview remote is not a valid URL." };
1235
+ }
1236
+ if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && LOOPBACK_HOSTS.has(parsed.hostname))) {
1237
+ return {
1238
+ ok: false,
1239
+ message: "A preview remote must use HTTPS or loopback HTTP."
1240
+ };
1241
+ }
1242
+ return { ok: true, url: parsed.toString() };
1243
+ }
1244
+ var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
1245
+ function messageDocument(title2, messages) {
1246
+ return `<!doctype html>
1247
+ <html lang="en">
1248
+ <head>
1249
+ <meta charset="UTF-8" />
1250
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1251
+ <title>${escapeHtml(title2)} \xB7 Tappify local preview</title>
1252
+ <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>
1253
+ </head>
1254
+ <body><main><h1>${escapeHtml(title2)}</h1><ul>${messages.map((message) => `<li>${escapeHtml(message)}</li>`).join("")}</ul></main></body>
1255
+ </html>`;
1256
+ }
1257
+ function previewDocument(manifest, remote) {
1258
+ if (!manifest.ok) {
1259
+ return messageDocument(
1260
+ "Manifest is invalid",
1261
+ manifest.issues.map((issue) => `${issue.path}: ${issue.message}`)
1262
+ );
1263
+ }
1264
+ if (!remote.ok)
1265
+ return messageDocument("Preview remote was refused", [remote.message]);
1266
+ const icon = manifest.manifest.icon.replace(/^\.\//, "");
1267
+ return `<!doctype html>
1268
+ <html lang="en">
1269
+ <head>
1270
+ <meta charset="UTF-8" />
1271
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1272
+ <link rel="icon" href="/${escapeHtml(icon)}" />
1273
+ <title>${escapeHtml(manifest.manifest.name)} \xB7 Tappify local preview</title>
1274
+ </head>
1275
+ <body>
1276
+ <div id="tappify-preview-root"></div>
1277
+ <script type="module" src="/@id/${PREVIEW_CLIENT_ID}"></script>
1278
+ </body>
1279
+ </html>`;
1280
+ }
1281
+ var moduleSource = (value) => `export default ${JSON.stringify(value)};`;
1282
+ function optimizedReactUrl(root, cacheDir, cacheGeneration) {
1283
+ const depsCacheDir = (0, import_vite.normalizePath)((0, import_node_path.resolve)(cacheDir, "deps"));
1284
+ const relativeCacheDir = (0, import_vite.normalizePath)((0, import_node_path.relative)(root, depsCacheDir));
1285
+ const prefix = (0, import_node_path.isAbsolute)(relativeCacheDir) || relativeCacheDir.startsWith("../") ? `/@fs/${depsCacheDir.replace(/^\/+/, "")}` : `/${relativeCacheDir}`;
1286
+ return `${prefix}/react.js?tappify=${encodeURIComponent(cacheGeneration)}`;
1287
+ }
1288
+ function createPreviewPlugins(options) {
1289
+ const remote = previewRemote(options.remote, options.port);
1290
+ const cacheGeneration = (0, import_node_crypto.randomUUID)();
1291
+ let reactUrl = optimizedReactUrl(
1292
+ (0, import_node_path.resolve)(options.manifestFile, ".."),
1293
+ (0, import_node_path.resolve)(options.manifestFile, "../node_modules/.vite"),
1294
+ cacheGeneration
1295
+ );
1296
+ const preview = {
1297
+ name: "tappify:preview",
1298
+ apply: "serve",
1299
+ config() {
1300
+ return {
1301
+ optimizeDeps: {
1302
+ exclude: ["@tappify/extension-sdk/vite/preview/bootstrap"]
1303
+ }
1304
+ };
1305
+ },
1306
+ configResolved(config) {
1307
+ reactUrl = optimizedReactUrl(
1308
+ config.root,
1309
+ config.cacheDir,
1310
+ cacheGeneration
1311
+ );
1312
+ },
1313
+ resolveId(id) {
1314
+ if (VIRTUAL_IDS.has(id)) return `\0${id}`;
1315
+ if (id.startsWith("\0") && VIRTUAL_IDS.has(id.slice(1))) return id;
1316
+ return null;
1317
+ },
1318
+ load(id) {
1319
+ if (id === `\0${PREVIEW_MANIFEST_ID}`) {
1320
+ return moduleSource(options.loadManifest());
1321
+ }
1322
+ if (id === `\0${PREVIEW_REMOTE_ID}`) return moduleSource(remote);
1323
+ if (id !== `\0${PREVIEW_CLIENT_ID}`) return null;
1324
+ return [
1325
+ `import { startLocalPreview } from '@tappify/extension-sdk/vite/preview/bootstrap';`,
1326
+ `import manifest from '${PREVIEW_MANIFEST_ID}';`,
1327
+ `import remote from '${PREVIEW_REMOTE_ID}';`,
1328
+ `const mount = document.getElementById('tappify-preview-root');`,
1329
+ `if (mount && manifest.ok && remote.ok) void startLocalPreview({ manifest: manifest.manifest, remote: remote.url, reactUrl: ${JSON.stringify(reactUrl)}, mount });`
1330
+ ].join("\n");
1331
+ },
1332
+ configureServer(server) {
1333
+ server.middlewares.use((request, response, next) => {
1334
+ const path = request.url?.split("?", 1)[0];
1335
+ if (path !== "/" && path !== "/index.html") return next();
1336
+ void server.transformIndexHtml(
1337
+ path,
1338
+ previewDocument(options.loadManifest(), remote)
1339
+ ).then((html) => {
1340
+ response.statusCode = 200;
1341
+ response.setHeader("Content-Type", "text/html; charset=utf-8");
1342
+ response.end(html);
1343
+ }).catch(next);
1344
+ });
1345
+ }
1346
+ };
1347
+ const dev = {
1348
+ name: "tappify:dev",
1349
+ apply: "serve",
1350
+ config() {
1351
+ return {
1352
+ server: {
1353
+ host: "127.0.0.1",
1354
+ port: options.port,
1355
+ strictPort: true,
1356
+ cors: { origin: true },
1357
+ origin: `http://localhost:${String(options.port)}`
1358
+ }
1359
+ };
1360
+ },
1361
+ configureServer(server) {
1362
+ server.watcher.add(options.manifestFile);
1363
+ },
1364
+ async hotUpdate(context) {
1365
+ if (this.environment.name !== "client") return;
1366
+ if (context.file !== options.manifestFile) return;
1367
+ const manifest = options.loadManifest();
1368
+ await options.onManifestChange?.(manifest);
1369
+ context.server.ws.send({ type: "full-reload", path: "*" });
1370
+ }
1371
+ };
1372
+ return [preview, dev];
1373
+ }
1127
1374
 
1128
1375
  // src/vite/styles.ts
1129
1376
  var import_node_fs = require("fs");
1130
- var import_node_path = require("path");
1377
+ var import_node_path2 = require("path");
1131
1378
  var IMPORT_PATTERN = /(?:^|\n)\s*(?:import|export)(?:[^'"]*?from\s*)?\s*['"]([^'"]+)['"]/g;
1132
1379
  var IMPORT_SPECIFIER_PATTERN = /((?:^|\n)\s*(?:import|export)(?:[^'"]*?from\s*)?\s*['"])([^'"]+)(['"])/g;
1133
1380
  var BLOCK_COMMENT_PATTERN = /\/\*[\s\S]*?\*\//g;
@@ -1140,20 +1387,20 @@ function isPlainStylesheet(specifier) {
1140
1387
  return specifier.endsWith(".css") && !specifier.endsWith(".module.css");
1141
1388
  }
1142
1389
  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;
1390
+ const base = (0, import_node_path2.resolve)((0, import_node_path2.dirname)(importer), specifier);
1391
+ if ((0, import_node_path2.extname)(base) !== "" && (0, import_node_fs.existsSync)(base)) return base;
1145
1392
  for (const extension of SOURCE_EXTENSIONS) {
1146
1393
  const candidate = `${base}${extension}`;
1147
1394
  if ((0, import_node_fs.existsSync)(candidate)) return candidate;
1148
1395
  }
1149
1396
  for (const extension of SOURCE_EXTENSIONS) {
1150
- const candidate = (0, import_node_path.resolve)(base, `index${extension}`);
1397
+ const candidate = (0, import_node_path2.resolve)(base, `index${extension}`);
1151
1398
  if ((0, import_node_fs.existsSync)(candidate)) return candidate;
1152
1399
  }
1153
1400
  return null;
1154
1401
  }
1155
1402
  function collectEntryStyles(entryAbsolutePath, root) {
1156
- const boundary = root.endsWith(import_node_path.sep) ? root : `${root}${import_node_path.sep}`;
1403
+ const boundary = root.endsWith(import_node_path2.sep) ? root : `${root}${import_node_path2.sep}`;
1157
1404
  const styles = [];
1158
1405
  const seenStyles = /* @__PURE__ */ new Set();
1159
1406
  const seenFiles = /* @__PURE__ */ new Set();
@@ -1169,7 +1416,7 @@ function collectEntryStyles(entryAbsolutePath, root) {
1169
1416
  while (match !== null) {
1170
1417
  const specifier = match[1];
1171
1418
  if (specifier.endsWith(".css")) {
1172
- const value = specifier.startsWith(".") ? (0, import_node_path.resolve)((0, import_node_path.dirname)(file), specifier) : specifier;
1419
+ const value = specifier.startsWith(".") ? (0, import_node_path2.resolve)((0, import_node_path2.dirname)(file), specifier) : specifier;
1173
1420
  if (!seenStyles.has(value)) {
1174
1421
  seenStyles.add(value);
1175
1422
  styles.push(value);
@@ -1202,38 +1449,52 @@ var DEFAULT_PORT = 5273;
1202
1449
  var SOURCE_FILE = /\.(?:[cm]?[jt]sx?)$/;
1203
1450
  var federationOptionsByPlugins = /* @__PURE__ */ new WeakMap();
1204
1451
  function manifestFile(options) {
1205
- return options.manifestPath ?? (0, import_node_path2.resolve)(options.root ?? process.cwd(), "tappify.extension.json");
1452
+ return options.manifestPath ?? (0, import_node_path3.resolve)(options.root ?? process.cwd(), "tappify.extension.json");
1206
1453
  }
1207
1454
  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:
1455
+ let raw;
1456
+ try {
1457
+ raw = options.manifest ?? JSON.parse((0, import_node_fs2.readFileSync)(manifestFile(options), "utf8"));
1458
+ } catch (error) {
1459
+ return {
1460
+ ok: false,
1461
+ issues: [
1462
+ {
1463
+ path: "$",
1464
+ message: error instanceof Error ? `The manifest is not valid JSON: ${error.message}` : "The manifest is not valid JSON."
1465
+ }
1466
+ ]
1467
+ };
1468
+ }
1469
+ return validateManifest(raw);
1470
+ }
1471
+ function manifestError(result) {
1472
+ if (result.ok) throw new Error("A valid manifest has no validation error.");
1473
+ const lines = result.issues.map((issue) => ` ${issue.path}: ${issue.message}`).join("\n");
1474
+ return new TapError(
1475
+ "TAP_MANIFEST_INVALID",
1476
+ `tappify.extension.json does not validate:
1215
1477
  ${lines}
1216
1478
  Fix the fields above, or run \`tappify extension doctor\` for the same list with suggested fixes.`
1217
- );
1218
- }
1219
- return result.manifest;
1479
+ );
1220
1480
  }
1221
1481
  function tappifyExtension(options = {}) {
1222
1482
  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);
1483
+ const boundary = root.endsWith(import_node_path3.sep) ? root : `${root}${import_node_path3.sep}`;
1484
+ const loaded = loadManifest(options);
1485
+ const manifest = loaded.ok ? loaded.manifest : null;
1486
+ const contributions = manifest === null ? [] : uiContributions(manifest);
1226
1487
  const port = options.port ?? DEFAULT_PORT;
1227
1488
  const entryByKey = /* @__PURE__ */ new Map();
1228
1489
  const exposes = {};
1229
1490
  for (const contribution of contributions) {
1230
1491
  const key = contribution.expose.slice("./".length);
1231
- entryByKey.set(key, (0, import_node_path2.resolve)(root, contribution.entry));
1492
+ entryByKey.set(key, (0, import_node_path3.resolve)(root, contribution.entry));
1232
1493
  exposes[contribution.expose] = `${VIRTUAL_PREFIX}${key}`;
1233
1494
  }
1234
1495
  const singleton = { singleton: true, requiredVersion: false };
1235
1496
  const federationOptions = {
1236
- name: remoteName(manifest.id),
1497
+ name: remoteName(manifest?.id ?? "invalid-extension"),
1237
1498
  filename: "remoteEntry.js",
1238
1499
  manifest: true,
1239
1500
  exposes,
@@ -1289,56 +1550,55 @@ function tappifyExtension(options = {}) {
1289
1550
  ].join("\n");
1290
1551
  }
1291
1552
  };
1553
+ const guard = {
1554
+ name: "tappify:manifest",
1555
+ enforce: "pre",
1556
+ config(_config, environment) {
1557
+ if (environment.command !== "build") return null;
1558
+ const current = loadManifest(options);
1559
+ if (!current.ok) throw manifestError(current);
1560
+ return null;
1561
+ }
1562
+ };
1292
1563
  const css = {
1293
1564
  name: "tappify:css",
1294
1565
  enforce: "pre",
1295
1566
  transform(code, id) {
1296
1567
  if (!id.startsWith(boundary)) return null;
1297
- if (id.includes(`${import_node_path2.sep}node_modules${import_node_path2.sep}`)) return null;
1568
+ if (id.includes(`${import_node_path3.sep}node_modules${import_node_path3.sep}`)) return null;
1298
1569
  const file = id.split("?")[0];
1299
1570
  if (!SOURCE_FILE.test(file)) return null;
1300
1571
  const next = inlineCssImports(code);
1301
1572
  return next === null ? null : { code: next, map: null };
1302
1573
  }
1303
1574
  };
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
1575
  const plugins = [
1576
+ guard,
1329
1577
  expose,
1330
1578
  css,
1331
- ...(0, import_vite.federation)(federationOptions),
1332
- dev
1579
+ ...(0, import_vite2.federation)(federationOptions),
1580
+ ...createPreviewPlugins({
1581
+ manifestFile: manifestFile(options),
1582
+ loadManifest: () => loadManifest(options),
1583
+ port,
1584
+ remote: process.env[PREVIEW_REMOTE_ENV],
1585
+ onManifestChange: async (result) => {
1586
+ if (result.ok && options.onManifestChange !== void 0) {
1587
+ await options.onManifestChange(result.manifest);
1588
+ }
1589
+ }
1590
+ })
1333
1591
  ];
1334
1592
  federationOptionsByPlugins.set(plugins, federationOptions);
1335
1593
  return plugins;
1336
1594
  }
1337
1595
  // Annotate the CommonJS export names for ESM import in node:
1338
1596
  0 && (module.exports = {
1597
+ PREVIEW_REMOTE_ENV,
1339
1598
  VIRTUAL_PREFIX,
1340
1599
  collectEntryStyles,
1341
1600
  inlineCssImports,
1601
+ previewRemote,
1342
1602
  tappifyExtension
1343
1603
  });
1344
1604
  //# sourceMappingURL=vite.cjs.map