@tappify/extension-sdk 2.1.0-beta.2 → 2.1.0-beta.3
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.
- package/dist/{chunk-QJMZRV7A.mjs → chunk-QQSCA6CC.mjs} +18 -6
- package/dist/chunk-QQSCA6CC.mjs.map +1 -0
- package/dist/{chunk-EB5XWQAU.cjs → chunk-SALMPL5V.cjs} +18 -6
- package/dist/chunk-SALMPL5V.cjs.map +1 -0
- package/dist/server.cjs +2 -2
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.mjs +1 -1
- package/dist/testing/index.cjs +2 -2
- package/dist/testing/index.d.cts +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.mjs +1 -1
- package/dist/vite-preview.cjs +1 -1
- package/dist/vite-preview.cjs.map +1 -1
- package/dist/vite-preview.mjs +1 -1
- package/dist/vite-preview.mjs.map +1 -1
- package/dist/{work-events-BlOVpK0k.d.ts → work-events-C5n497rx.d.ts} +38 -6
- package/dist/{work-events-D97EoqvS.d.cts → work-events-DSklu4t-.d.cts} +38 -6
- package/package.json +1 -1
- package/dist/chunk-EB5XWQAU.cjs.map +0 -1
- package/dist/chunk-QJMZRV7A.mjs.map +0 -1
|
@@ -68,6 +68,20 @@ async function sendEvent(name, payload, options) {
|
|
|
68
68
|
function nonempty(value) {
|
|
69
69
|
return typeof value === "string" && value.trim().length > 0;
|
|
70
70
|
}
|
|
71
|
+
function validPerson(person) {
|
|
72
|
+
const email = readUnknown(person, "email");
|
|
73
|
+
return nonempty(readUnknown(person, "id")) && nonempty(readUnknown(person, "displayName")) && (email === void 0 || nonempty(email) && email === email.trim() && email.split("@").length === 2);
|
|
74
|
+
}
|
|
75
|
+
function validSegment(segment) {
|
|
76
|
+
const type = readUnknown(segment, "type");
|
|
77
|
+
if (type === "text") return typeof readUnknown(segment, "text") === "string";
|
|
78
|
+
return type === "user_mention" && validPerson(readUnknown(segment, "person")) && nonempty(readUnknown(segment, "fallback"));
|
|
79
|
+
}
|
|
80
|
+
function validComment(comment) {
|
|
81
|
+
const author = readUnknown(comment, "author");
|
|
82
|
+
const segments = readUnknown(comment, "segments");
|
|
83
|
+
return nonempty(readUnknown(comment, "externalCommentId")) && typeof readUnknown(comment, "body") === "string" && nonempty(readUnknown(comment, "createdAt")) && validPerson(author) && (segments === void 0 ? readUnknown(author, "email") === void 0 : Array.isArray(segments) && Array.from(segments).every(validSegment));
|
|
84
|
+
}
|
|
71
85
|
function validEvent(event) {
|
|
72
86
|
const type = readText(event, "type");
|
|
73
87
|
if (!["comment.created", "status.changed", "item.deleted"].includes(type ?? ""))
|
|
@@ -84,12 +98,10 @@ function validEvent(event) {
|
|
|
84
98
|
if (type === "item.deleted")
|
|
85
99
|
return comment === void 0 && status === void 0;
|
|
86
100
|
if (type === "status.changed") {
|
|
87
|
-
const
|
|
88
|
-
return comment === void 0 && nonempty(
|
|
101
|
+
const normalized = readRecord(event, "status");
|
|
102
|
+
return comment === void 0 && nonempty(normalized.id) && nonempty(normalized.label) && typeof normalized.stage === "string" && ["open", "in_progress", "done"].includes(normalized.stage);
|
|
89
103
|
}
|
|
90
|
-
|
|
91
|
-
const author = readRecord(normalized, "author");
|
|
92
|
-
return status === void 0 && nonempty(normalized.externalCommentId) && typeof normalized.body === "string" && nonempty(normalized.createdAt) && nonempty(author.id) && nonempty(author.displayName);
|
|
104
|
+
return status === void 0 && validComment(comment);
|
|
93
105
|
}
|
|
94
106
|
async function sendWorkEvent(event, options) {
|
|
95
107
|
if (!validEvent(event))
|
|
@@ -129,4 +141,4 @@ export {
|
|
|
129
141
|
sendEvent,
|
|
130
142
|
sendWorkEvent
|
|
131
143
|
};
|
|
132
|
-
//# sourceMappingURL=chunk-
|
|
144
|
+
//# sourceMappingURL=chunk-QQSCA6CC.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/events.ts","../src/server/work-events.ts"],"sourcesContent":["import { TapServerError } from '../client/errors';\nimport type { WebhookName, WebhookPayload } from '../client/types';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { readRecord, readText } from './wire';\n\n/** The body `sendEvent` posts to Tappify, and the body the signature covers. */\nexport interface WebhookEnvelope {\n /** The declared webhook name. */\n event: string;\n /** The extension id the event came from. */\n source: string;\n /** When the event happened, as an ISO timestamp. */\n occurredAt: string;\n /** The build the event belongs to, when it belongs to one. */\n build?: string;\n /** The key Tappify deduplicates on, so a retry is not counted twice. */\n dedupeKey: string;\n payload: Record<string, unknown>;\n}\n\n/** The third argument of `sendEvent`. */\nexport interface SendEventOptions {\n /** Which install the event belongs to. */\n installId: string;\n extensionId: string;\n /** The inbound signing secret from the extension's Server page. */\n secret: string;\n /** A full url to post to, which replaces `baseUrl` and the built path. */\n endpoint?: string;\n /** The Tappify origin to build the path under. Defaults to production. */\n baseUrl?: string;\n /** Defaults to a fresh UUID, so give your own to make a retry idempotent. */\n dedupeKey?: string;\n build?: string;\n /** Defaults to now, in ISO form. */\n occurredAt?: string;\n /** A `fetch` of your own, for a test or a proxy. Defaults to the global one. */\n fetch?: typeof fetch;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.tappify.ai';\n\nfunction toHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map(byte => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Returns the hex HMAC-SHA-256 of a raw body under a shared secret.\n *\n * @remarks\n * This is the value `sendEvent` sends as `X-Tappify-Signature`, computed over the\n * exact bytes of the body it posts. Call it directly only when you build the\n * request yourself, and sign the serialised string you send rather than\n * re-serialising the object, because a different key order is a different\n * signature. Uses Web Crypto, so it runs on Node, Cloudflare Workers and Vercel\n * alike.\n *\n * @example\n * ```ts\n * import { signWebhook } from '@tappify/extension-sdk/server';\n *\n * async function signed(body: string): Promise<Record<string, string>> {\n * return { 'x-tappify-signature': await signWebhook('whsec_test', body) };\n * }\n * ```\n */\nexport async function signWebhook(\n secret: string,\n body: string,\n): Promise<string> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(body));\n return toHex(signature);\n}\n\nasync function rejectionCode(response: Response): Promise<string | null> {\n try {\n const payload: unknown = await response.json();\n return readText(readRecord(payload, 'error'), 'code');\n } catch {\n return null;\n }\n}\n\n/**\n * Posts one of your declared webhook events to Tappify, signed with the install's\n * inbound secret.\n *\n * @remarks\n * The name and the payload are narrowed by the `TapWebhookMap` augmentation\n * `tappify extension types` writes. It builds the envelope, signs the serialised\n * body and posts it to the install's hook path under `baseUrl`, or to `endpoint`\n * when you give one. A response Tappify did not accept rejects with a\n * `TapServerError` whose `code` is always `TAP_WEBHOOK_REJECTED`, carrying the HTTP\n * status, and with Tappify's own code in the message text rather than in `code`;\n * the two usual causes are the wrong signing\n * secret and an event the manifest does not declare. Pass your own `dedupeKey` to\n * make a retry idempotent, because the default is a fresh UUID.\n *\n * @example\n * ```ts\n * import { sendEvent } from '@tappify/extension-sdk/server';\n *\n * async function reportAnomaly(installId: string, why: string): Promise<void> {\n * await sendEvent('anomaly.detected', { why }, {\n * installId,\n * extensionId: 'starter',\n * secret: process.env.TAPPIFY_INBOUND_SECRET ?? '',\n * dedupeKey: `anomaly-${installId}-${why}`,\n * });\n * }\n * ```\n */\nexport async function sendEvent<E extends WebhookName>(\n name: E,\n payload: WebhookPayload<E>,\n options: SendEventOptions,\n): Promise<void> {\n const envelope: WebhookEnvelope = {\n event: name,\n source: options.extensionId,\n occurredAt: options.occurredAt ?? new Date().toISOString(),\n dedupeKey: options.dedupeKey ?? crypto.randomUUID(),\n payload: { ...payload },\n };\n\n if (options.build !== undefined) envelope.build = options.build;\n\n const body = JSON.stringify(envelope);\n const signature = await signWebhook(options.secret, body);\n const url =\n options.endpoint ??\n `${options.baseUrl ?? DEFAULT_BASE_URL}/api/v1/extensions/hooks/${options.extensionId}/${options.installId}`;\n\n const send = options.fetch ?? fetch;\n const response = await send(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n const rejection = await rejectionCode(response);\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for ${name}. Check the inbound signing secret on the extension's Server page, and that the event is declared under contributes.webhooks.`,\n response.status,\n );\n }\n}\n","import { TapServerError } from '../client/errors';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { signWebhook } from './events';\nimport { readRecord, readText, readUnknown } from './wire';\nimport type { WorkHydratedEvent, WorkHydratedEventBase } from './work';\n\n/** An opaque provider signal to hydrate using current install credentials. */\nexport interface WorkHydrationReferenceEvent extends WorkHydratedEventBase {\n type: WorkHydratedEvent['type'];\n /** A nonempty, short-lived reference resolved by the hydrate_event handler. */\n hydrateRef: string;\n /** Normalized comment content must not accompany a reference. */\n comment?: never;\n /** Normalized status content must not accompany a reference. */\n status?: never;\n}\n\n/** Complete normalized content or an opaque reference, with stable retry identity. */\nexport type WorkEvent =\n | (WorkHydratedEvent & { hydrateRef?: never })\n | WorkHydrationReferenceEvent;\n\n/** Install-specific delivery values provisioned through work configure. */\nexport interface SendWorkEventOptions {\n /** The exact provisioned callback URL; no production default is supplied. */\n callbackUrl: string;\n /** The per-install work-event signing secret provisioned with the callback. */\n secret: string;\n /** A transport for tests or a proxy; defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nfunction nonempty(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction validPerson(person: unknown): boolean {\n const email = readUnknown(person, 'email');\n return (\n nonempty(readUnknown(person, 'id')) &&\n nonempty(readUnknown(person, 'displayName')) &&\n (email === undefined ||\n (nonempty(email) &&\n email === email.trim() &&\n email.split('@').length === 2))\n );\n}\n\nfunction validSegment(segment: unknown): boolean {\n const type = readUnknown(segment, 'type');\n if (type === 'text') return typeof readUnknown(segment, 'text') === 'string';\n return (\n type === 'user_mention' &&\n validPerson(readUnknown(segment, 'person')) &&\n nonempty(readUnknown(segment, 'fallback'))\n );\n}\n\nfunction validComment(comment: unknown): boolean {\n const author = readUnknown(comment, 'author');\n const segments = readUnknown(comment, 'segments');\n return (\n nonempty(readUnknown(comment, 'externalCommentId')) &&\n typeof readUnknown(comment, 'body') === 'string' &&\n nonempty(readUnknown(comment, 'createdAt')) &&\n validPerson(author) &&\n (segments === undefined\n ? readUnknown(author, 'email') === undefined\n : Array.isArray(segments) && Array.from(segments).every(validSegment))\n );\n}\n\nfunction validEvent(event: WorkEvent): boolean {\n const type = readText(event, 'type');\n if (\n !['comment.created', 'status.changed', 'item.deleted'].includes(type ?? '')\n )\n return false;\n if (\n !['externalItemId', 'providerEventId', 'occurredAt', 'dedupeKey'].every(\n key => nonempty(readUnknown(event, key)),\n )\n )\n return false;\n\n const reference = readUnknown(event, 'hydrateRef');\n const comment = readUnknown(event, 'comment');\n const status = readUnknown(event, 'status');\n if (reference !== undefined)\n return nonempty(reference) && comment === undefined && status === undefined;\n\n if (type === 'item.deleted')\n return comment === undefined && status === undefined;\n if (type === 'status.changed') {\n const normalized = readRecord(event, 'status');\n return (\n comment === undefined &&\n nonempty(normalized.id) &&\n nonempty(normalized.label) &&\n typeof normalized.stage === 'string' &&\n ['open', 'in_progress', 'done'].includes(normalized.stage)\n );\n }\n\n return status === undefined && validComment(comment);\n}\n\n/**\n * Sends one provider work event to the callback provisioned for this install.\n *\n * @remarks\n * Pass the callbackUrl and secret received through configure unchanged. The\n * event's required dedupeKey identifies the logical provider signal and must be\n * reused on retries. Complete normalized content and an opaque hydrateRef are\n * mutually exclusive. The exact serialized UTF-8 body is signed with HMAC-SHA-256.\n * This callback is independent of declared alert and banner webhooks.\n */\nexport async function sendWorkEvent(\n event: WorkEvent,\n options: SendWorkEventOptions,\n): Promise<void> {\n if (!validEvent(event))\n throw new TypeError(\n 'Invalid work event: provide complete normalized content or a nonempty hydrateRef, with routing and identity fields.',\n );\n if (!nonempty(options.callbackUrl))\n throw new TypeError(\n 'A work event requires the callbackUrl provisioned through configure.',\n );\n\n const body = JSON.stringify(event);\n const signature = await signWebhook(options.secret, body);\n const response = await (options.fetch ?? fetch)(options.callbackUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n let rejection: string | null = null;\n try {\n rejection = readText(readRecord(await response.json(), 'error'), 'code');\n } catch {\n // Proxies may return a non-JSON rejection body.\n }\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for work event ${event.type}. Check the callback URL and per-install work-event signing secret provisioned through configure.`,\n response.status,\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -68,6 +68,20 @@ async function sendEvent(name, payload, options) {
|
|
|
68
68
|
function nonempty(value) {
|
|
69
69
|
return typeof value === "string" && value.trim().length > 0;
|
|
70
70
|
}
|
|
71
|
+
function validPerson(person) {
|
|
72
|
+
const email = _chunkA74HCO6Tcjs.readUnknown.call(void 0, person, "email");
|
|
73
|
+
return nonempty(_chunkA74HCO6Tcjs.readUnknown.call(void 0, person, "id")) && nonempty(_chunkA74HCO6Tcjs.readUnknown.call(void 0, person, "displayName")) && (email === void 0 || nonempty(email) && email === email.trim() && email.split("@").length === 2);
|
|
74
|
+
}
|
|
75
|
+
function validSegment(segment) {
|
|
76
|
+
const type = _chunkA74HCO6Tcjs.readUnknown.call(void 0, segment, "type");
|
|
77
|
+
if (type === "text") return typeof _chunkA74HCO6Tcjs.readUnknown.call(void 0, segment, "text") === "string";
|
|
78
|
+
return type === "user_mention" && validPerson(_chunkA74HCO6Tcjs.readUnknown.call(void 0, segment, "person")) && nonempty(_chunkA74HCO6Tcjs.readUnknown.call(void 0, segment, "fallback"));
|
|
79
|
+
}
|
|
80
|
+
function validComment(comment) {
|
|
81
|
+
const author = _chunkA74HCO6Tcjs.readUnknown.call(void 0, comment, "author");
|
|
82
|
+
const segments = _chunkA74HCO6Tcjs.readUnknown.call(void 0, comment, "segments");
|
|
83
|
+
return nonempty(_chunkA74HCO6Tcjs.readUnknown.call(void 0, comment, "externalCommentId")) && typeof _chunkA74HCO6Tcjs.readUnknown.call(void 0, comment, "body") === "string" && nonempty(_chunkA74HCO6Tcjs.readUnknown.call(void 0, comment, "createdAt")) && validPerson(author) && (segments === void 0 ? _chunkA74HCO6Tcjs.readUnknown.call(void 0, author, "email") === void 0 : Array.isArray(segments) && Array.from(segments).every(validSegment));
|
|
84
|
+
}
|
|
71
85
|
function validEvent(event) {
|
|
72
86
|
const type = _chunkA74HCO6Tcjs.readText.call(void 0, event, "type");
|
|
73
87
|
if (!["comment.created", "status.changed", "item.deleted"].includes(_nullishCoalesce(type, () => ( ""))))
|
|
@@ -84,12 +98,10 @@ function validEvent(event) {
|
|
|
84
98
|
if (type === "item.deleted")
|
|
85
99
|
return comment === void 0 && status === void 0;
|
|
86
100
|
if (type === "status.changed") {
|
|
87
|
-
const
|
|
88
|
-
return comment === void 0 && nonempty(
|
|
101
|
+
const normalized = _chunkA74HCO6Tcjs.readRecord.call(void 0, event, "status");
|
|
102
|
+
return comment === void 0 && nonempty(normalized.id) && nonempty(normalized.label) && typeof normalized.stage === "string" && ["open", "in_progress", "done"].includes(normalized.stage);
|
|
89
103
|
}
|
|
90
|
-
|
|
91
|
-
const author = _chunkA74HCO6Tcjs.readRecord.call(void 0, normalized, "author");
|
|
92
|
-
return status === void 0 && nonempty(normalized.externalCommentId) && typeof normalized.body === "string" && nonempty(normalized.createdAt) && nonempty(author.id) && nonempty(author.displayName);
|
|
104
|
+
return status === void 0 && validComment(comment);
|
|
93
105
|
}
|
|
94
106
|
async function sendWorkEvent(event, options) {
|
|
95
107
|
if (!validEvent(event))
|
|
@@ -129,4 +141,4 @@ async function sendWorkEvent(event, options) {
|
|
|
129
141
|
|
|
130
142
|
|
|
131
143
|
exports.signWebhook = signWebhook; exports.sendEvent = sendEvent; exports.sendWorkEvent = sendWorkEvent;
|
|
132
|
-
//# sourceMappingURL=chunk-
|
|
144
|
+
//# sourceMappingURL=chunk-SALMPL5V.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/chunk-SALMPL5V.cjs","../src/server/events.ts","../src/server/work-events.ts"],"names":[],"mappings":"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","file":"/home/runner/work/extension-sdk/extension-sdk/dist/chunk-SALMPL5V.cjs","sourcesContent":[null,"import { TapServerError } from '../client/errors';\nimport type { WebhookName, WebhookPayload } from '../client/types';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { readRecord, readText } from './wire';\n\n/** The body `sendEvent` posts to Tappify, and the body the signature covers. */\nexport interface WebhookEnvelope {\n /** The declared webhook name. */\n event: string;\n /** The extension id the event came from. */\n source: string;\n /** When the event happened, as an ISO timestamp. */\n occurredAt: string;\n /** The build the event belongs to, when it belongs to one. */\n build?: string;\n /** The key Tappify deduplicates on, so a retry is not counted twice. */\n dedupeKey: string;\n payload: Record<string, unknown>;\n}\n\n/** The third argument of `sendEvent`. */\nexport interface SendEventOptions {\n /** Which install the event belongs to. */\n installId: string;\n extensionId: string;\n /** The inbound signing secret from the extension's Server page. */\n secret: string;\n /** A full url to post to, which replaces `baseUrl` and the built path. */\n endpoint?: string;\n /** The Tappify origin to build the path under. Defaults to production. */\n baseUrl?: string;\n /** Defaults to a fresh UUID, so give your own to make a retry idempotent. */\n dedupeKey?: string;\n build?: string;\n /** Defaults to now, in ISO form. */\n occurredAt?: string;\n /** A `fetch` of your own, for a test or a proxy. Defaults to the global one. */\n fetch?: typeof fetch;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.tappify.ai';\n\nfunction toHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map(byte => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Returns the hex HMAC-SHA-256 of a raw body under a shared secret.\n *\n * @remarks\n * This is the value `sendEvent` sends as `X-Tappify-Signature`, computed over the\n * exact bytes of the body it posts. Call it directly only when you build the\n * request yourself, and sign the serialised string you send rather than\n * re-serialising the object, because a different key order is a different\n * signature. Uses Web Crypto, so it runs on Node, Cloudflare Workers and Vercel\n * alike.\n *\n * @example\n * ```ts\n * import { signWebhook } from '@tappify/extension-sdk/server';\n *\n * async function signed(body: string): Promise<Record<string, string>> {\n * return { 'x-tappify-signature': await signWebhook('whsec_test', body) };\n * }\n * ```\n */\nexport async function signWebhook(\n secret: string,\n body: string,\n): Promise<string> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(body));\n return toHex(signature);\n}\n\nasync function rejectionCode(response: Response): Promise<string | null> {\n try {\n const payload: unknown = await response.json();\n return readText(readRecord(payload, 'error'), 'code');\n } catch {\n return null;\n }\n}\n\n/**\n * Posts one of your declared webhook events to Tappify, signed with the install's\n * inbound secret.\n *\n * @remarks\n * The name and the payload are narrowed by the `TapWebhookMap` augmentation\n * `tappify extension types` writes. It builds the envelope, signs the serialised\n * body and posts it to the install's hook path under `baseUrl`, or to `endpoint`\n * when you give one. A response Tappify did not accept rejects with a\n * `TapServerError` whose `code` is always `TAP_WEBHOOK_REJECTED`, carrying the HTTP\n * status, and with Tappify's own code in the message text rather than in `code`;\n * the two usual causes are the wrong signing\n * secret and an event the manifest does not declare. Pass your own `dedupeKey` to\n * make a retry idempotent, because the default is a fresh UUID.\n *\n * @example\n * ```ts\n * import { sendEvent } from '@tappify/extension-sdk/server';\n *\n * async function reportAnomaly(installId: string, why: string): Promise<void> {\n * await sendEvent('anomaly.detected', { why }, {\n * installId,\n * extensionId: 'starter',\n * secret: process.env.TAPPIFY_INBOUND_SECRET ?? '',\n * dedupeKey: `anomaly-${installId}-${why}`,\n * });\n * }\n * ```\n */\nexport async function sendEvent<E extends WebhookName>(\n name: E,\n payload: WebhookPayload<E>,\n options: SendEventOptions,\n): Promise<void> {\n const envelope: WebhookEnvelope = {\n event: name,\n source: options.extensionId,\n occurredAt: options.occurredAt ?? new Date().toISOString(),\n dedupeKey: options.dedupeKey ?? crypto.randomUUID(),\n payload: { ...payload },\n };\n\n if (options.build !== undefined) envelope.build = options.build;\n\n const body = JSON.stringify(envelope);\n const signature = await signWebhook(options.secret, body);\n const url =\n options.endpoint ??\n `${options.baseUrl ?? DEFAULT_BASE_URL}/api/v1/extensions/hooks/${options.extensionId}/${options.installId}`;\n\n const send = options.fetch ?? fetch;\n const response = await send(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n const rejection = await rejectionCode(response);\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for ${name}. Check the inbound signing secret on the extension's Server page, and that the event is declared under contributes.webhooks.`,\n response.status,\n );\n }\n}\n","import { TapServerError } from '../client/errors';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { signWebhook } from './events';\nimport { readRecord, readText, readUnknown } from './wire';\nimport type { WorkHydratedEvent, WorkHydratedEventBase } from './work';\n\n/** An opaque provider signal to hydrate using current install credentials. */\nexport interface WorkHydrationReferenceEvent extends WorkHydratedEventBase {\n type: WorkHydratedEvent['type'];\n /** A nonempty, short-lived reference resolved by the hydrate_event handler. */\n hydrateRef: string;\n /** Normalized comment content must not accompany a reference. */\n comment?: never;\n /** Normalized status content must not accompany a reference. */\n status?: never;\n}\n\n/** Complete normalized content or an opaque reference, with stable retry identity. */\nexport type WorkEvent =\n | (WorkHydratedEvent & { hydrateRef?: never })\n | WorkHydrationReferenceEvent;\n\n/** Install-specific delivery values provisioned through work configure. */\nexport interface SendWorkEventOptions {\n /** The exact provisioned callback URL; no production default is supplied. */\n callbackUrl: string;\n /** The per-install work-event signing secret provisioned with the callback. */\n secret: string;\n /** A transport for tests or a proxy; defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nfunction nonempty(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction validPerson(person: unknown): boolean {\n const email = readUnknown(person, 'email');\n return (\n nonempty(readUnknown(person, 'id')) &&\n nonempty(readUnknown(person, 'displayName')) &&\n (email === undefined ||\n (nonempty(email) &&\n email === email.trim() &&\n email.split('@').length === 2))\n );\n}\n\nfunction validSegment(segment: unknown): boolean {\n const type = readUnknown(segment, 'type');\n if (type === 'text') return typeof readUnknown(segment, 'text') === 'string';\n return (\n type === 'user_mention' &&\n validPerson(readUnknown(segment, 'person')) &&\n nonempty(readUnknown(segment, 'fallback'))\n );\n}\n\nfunction validComment(comment: unknown): boolean {\n const author = readUnknown(comment, 'author');\n const segments = readUnknown(comment, 'segments');\n return (\n nonempty(readUnknown(comment, 'externalCommentId')) &&\n typeof readUnknown(comment, 'body') === 'string' &&\n nonempty(readUnknown(comment, 'createdAt')) &&\n validPerson(author) &&\n (segments === undefined\n ? readUnknown(author, 'email') === undefined\n : Array.isArray(segments) && Array.from(segments).every(validSegment))\n );\n}\n\nfunction validEvent(event: WorkEvent): boolean {\n const type = readText(event, 'type');\n if (\n !['comment.created', 'status.changed', 'item.deleted'].includes(type ?? '')\n )\n return false;\n if (\n !['externalItemId', 'providerEventId', 'occurredAt', 'dedupeKey'].every(\n key => nonempty(readUnknown(event, key)),\n )\n )\n return false;\n\n const reference = readUnknown(event, 'hydrateRef');\n const comment = readUnknown(event, 'comment');\n const status = readUnknown(event, 'status');\n if (reference !== undefined)\n return nonempty(reference) && comment === undefined && status === undefined;\n\n if (type === 'item.deleted')\n return comment === undefined && status === undefined;\n if (type === 'status.changed') {\n const normalized = readRecord(event, 'status');\n return (\n comment === undefined &&\n nonempty(normalized.id) &&\n nonempty(normalized.label) &&\n typeof normalized.stage === 'string' &&\n ['open', 'in_progress', 'done'].includes(normalized.stage)\n );\n }\n\n return status === undefined && validComment(comment);\n}\n\n/**\n * Sends one provider work event to the callback provisioned for this install.\n *\n * @remarks\n * Pass the callbackUrl and secret received through configure unchanged. The\n * event's required dedupeKey identifies the logical provider signal and must be\n * reused on retries. Complete normalized content and an opaque hydrateRef are\n * mutually exclusive. The exact serialized UTF-8 body is signed with HMAC-SHA-256.\n * This callback is independent of declared alert and banner webhooks.\n */\nexport async function sendWorkEvent(\n event: WorkEvent,\n options: SendWorkEventOptions,\n): Promise<void> {\n if (!validEvent(event))\n throw new TypeError(\n 'Invalid work event: provide complete normalized content or a nonempty hydrateRef, with routing and identity fields.',\n );\n if (!nonempty(options.callbackUrl))\n throw new TypeError(\n 'A work event requires the callbackUrl provisioned through configure.',\n );\n\n const body = JSON.stringify(event);\n const signature = await signWebhook(options.secret, body);\n const response = await (options.fetch ?? fetch)(options.callbackUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n let rejection: string | null = null;\n try {\n rejection = readText(readRecord(await response.json(), 'error'), 'code');\n } catch {\n // Proxies may return a non-JSON rejection body.\n }\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for work event ${event.type}. Check the callback URL and per-install work-event signing secret provisioned through configure.`,\n response.status,\n );\n }\n}\n"]}
|
package/dist/server.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkSALMPL5Vcjs = require('./chunk-SALMPL5V.cjs');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
@@ -155,5 +155,5 @@ function toNode(handler) {
|
|
|
155
155
|
|
|
156
156
|
|
|
157
157
|
|
|
158
|
-
exports.DEFAULT_JWKS_URL = _chunkA74HCO6Tcjs.DEFAULT_JWKS_URL; exports.SERVER_ERROR_CODES = _chunkA74HCO6Tcjs.SERVER_ERROR_CODES; exports.TapServerError = _chunk6HETNZJYcjs.TapServerError; exports.createTappifyHandler = _chunkA74HCO6Tcjs.createTappifyHandler; exports.sendEvent =
|
|
158
|
+
exports.DEFAULT_JWKS_URL = _chunkA74HCO6Tcjs.DEFAULT_JWKS_URL; exports.SERVER_ERROR_CODES = _chunkA74HCO6Tcjs.SERVER_ERROR_CODES; exports.TapServerError = _chunk6HETNZJYcjs.TapServerError; exports.createTappifyHandler = _chunkA74HCO6Tcjs.createTappifyHandler; exports.sendEvent = _chunkSALMPL5Vcjs.sendEvent; exports.sendWorkEvent = _chunkSALMPL5Vcjs.sendWorkEvent; exports.signWebhook = _chunkSALMPL5Vcjs.signWebhook; exports.toExpress = toExpress; exports.toNode = toNode; exports.verifyTappifyToken = _chunkA74HCO6Tcjs.verifyTappifyToken;
|
|
159
159
|
//# sourceMappingURL=server.cjs.map
|
package/dist/server.d.cts
CHANGED
|
@@ -2,8 +2,8 @@ export { c as TapServerError } from './errors-Cqh0wrqt.cjs';
|
|
|
2
2
|
import { ad as WebhookName, ae as WebhookPayload } from './types-BdDaLnqs.cjs';
|
|
3
3
|
export { A as ActionId, k as ActionInput, w as TapCredentialValues, a as TapFilters, c as TapHostEvents, $ as TapSettingsValues, ab as ToolId, ac as ToolInput } from './types-BdDaLnqs.cjs';
|
|
4
4
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
-
import { T as TappifyFetchHandler, V as VerifyTokenOptions, a as TappifyClaims } from './work-events-
|
|
6
|
-
export { C as ContextBlock, L as LegacyWorkHandlers, M as MentionItems, b as MetricsInput, c as MetricsResponse, d as MetricsSeries, R as ReviewAttachmentDescriptor, e as ReviewImageAttachmentDescriptor, f as ReviewLinkAttachmentDescriptor, g as ReviewThreadSource, S as SendWorkEventOptions, h as TappifyCallContext, i as TappifyDocuments, j as TappifyHandlerOptions, k as TappifyHealth, l as TappifyInstall, m as TappifyRequest, n as TappifyRouteHandler, W as WorkAttachInput, o as WorkAttachResult, p as WorkCommentInput, q as WorkCommentResult, r as WorkConfigureInput, s as WorkConfigureResult, t as WorkContainer, u as WorkCreateField, v as WorkCreateFieldValues, w as WorkCreateInput, x as WorkDescribeContainerInput, y as WorkDescribeContainerResult, z as WorkDisconnectInput, A as WorkDisconnectResult, B as WorkEvent, D as WorkHydrateEventInput, E as WorkHydrateEventResult, F as WorkHydratedCommentCreatedEvent, G as WorkHydratedEvent, H as WorkHydratedEventBase, I as WorkHydratedItemDeletedEvent, J as WorkHydratedStatusChangedEvent, K as WorkHydrationReferenceEvent, N as WorkItemLinkResult, O as WorkItemType, P as WorkLinkedItemInput, Q as WorkListContainersInput, U as WorkListContainersResult, X as
|
|
5
|
+
import { T as TappifyFetchHandler, V as VerifyTokenOptions, a as TappifyClaims } from './work-events-DSklu4t-.cjs';
|
|
6
|
+
export { C as ContextBlock, L as LegacyWorkHandlers, M as MentionItems, b as MetricsInput, c as MetricsResponse, d as MetricsSeries, R as ReviewAttachmentDescriptor, e as ReviewImageAttachmentDescriptor, f as ReviewLinkAttachmentDescriptor, g as ReviewThreadSource, S as SendWorkEventOptions, h as TappifyCallContext, i as TappifyDocuments, j as TappifyHandlerOptions, k as TappifyHealth, l as TappifyInstall, m as TappifyRequest, n as TappifyRouteHandler, W as WorkAttachInput, o as WorkAttachResult, p as WorkCommentInput, q as WorkCommentResult, r as WorkConfigureInput, s as WorkConfigureResult, t as WorkContainer, u as WorkCreateField, v as WorkCreateFieldValues, w as WorkCreateInput, x as WorkDescribeContainerInput, y as WorkDescribeContainerResult, z as WorkDisconnectInput, A as WorkDisconnectResult, B as WorkEvent, D as WorkHydrateEventInput, E as WorkHydrateEventResult, F as WorkHydratedCommentCreatedEvent, G as WorkHydratedEvent, H as WorkHydratedEventBase, I as WorkHydratedItemDeletedEvent, J as WorkHydratedStatusChangedEvent, K as WorkHydrationReferenceEvent, N as WorkItemLinkResult, O as WorkItemType, P as WorkLinkedItemInput, Q as WorkListContainersInput, U as WorkListContainersResult, X as WorkMention, Y as WorkMutationInput, Z as WorkOperation, _ as WorkOperationMap, $ as WorkPerson, a0 as WorkRichTextSegment, a1 as WorkSetStatusInput, a2 as WorkSetStatusResult, a3 as WorkStage, a4 as WorkStatus, a5 as createTappifyHandler, a6 as sendWorkEvent } from './work-events-DSklu4t-.cjs';
|
|
7
7
|
import './constants-Bh759FUI.cjs';
|
|
8
8
|
import 'jose';
|
|
9
9
|
|
package/dist/server.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { c as TapServerError } from './errors-Cqh0wrqt.js';
|
|
|
2
2
|
import { ad as WebhookName, ae as WebhookPayload } from './types-uGHrRaOj.js';
|
|
3
3
|
export { A as ActionId, k as ActionInput, w as TapCredentialValues, a as TapFilters, c as TapHostEvents, $ as TapSettingsValues, ab as ToolId, ac as ToolInput } from './types-uGHrRaOj.js';
|
|
4
4
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
-
import { T as TappifyFetchHandler, V as VerifyTokenOptions, a as TappifyClaims } from './work-events-
|
|
6
|
-
export { C as ContextBlock, L as LegacyWorkHandlers, M as MentionItems, b as MetricsInput, c as MetricsResponse, d as MetricsSeries, R as ReviewAttachmentDescriptor, e as ReviewImageAttachmentDescriptor, f as ReviewLinkAttachmentDescriptor, g as ReviewThreadSource, S as SendWorkEventOptions, h as TappifyCallContext, i as TappifyDocuments, j as TappifyHandlerOptions, k as TappifyHealth, l as TappifyInstall, m as TappifyRequest, n as TappifyRouteHandler, W as WorkAttachInput, o as WorkAttachResult, p as WorkCommentInput, q as WorkCommentResult, r as WorkConfigureInput, s as WorkConfigureResult, t as WorkContainer, u as WorkCreateField, v as WorkCreateFieldValues, w as WorkCreateInput, x as WorkDescribeContainerInput, y as WorkDescribeContainerResult, z as WorkDisconnectInput, A as WorkDisconnectResult, B as WorkEvent, D as WorkHydrateEventInput, E as WorkHydrateEventResult, F as WorkHydratedCommentCreatedEvent, G as WorkHydratedEvent, H as WorkHydratedEventBase, I as WorkHydratedItemDeletedEvent, J as WorkHydratedStatusChangedEvent, K as WorkHydrationReferenceEvent, N as WorkItemLinkResult, O as WorkItemType, P as WorkLinkedItemInput, Q as WorkListContainersInput, U as WorkListContainersResult, X as
|
|
5
|
+
import { T as TappifyFetchHandler, V as VerifyTokenOptions, a as TappifyClaims } from './work-events-C5n497rx.js';
|
|
6
|
+
export { C as ContextBlock, L as LegacyWorkHandlers, M as MentionItems, b as MetricsInput, c as MetricsResponse, d as MetricsSeries, R as ReviewAttachmentDescriptor, e as ReviewImageAttachmentDescriptor, f as ReviewLinkAttachmentDescriptor, g as ReviewThreadSource, S as SendWorkEventOptions, h as TappifyCallContext, i as TappifyDocuments, j as TappifyHandlerOptions, k as TappifyHealth, l as TappifyInstall, m as TappifyRequest, n as TappifyRouteHandler, W as WorkAttachInput, o as WorkAttachResult, p as WorkCommentInput, q as WorkCommentResult, r as WorkConfigureInput, s as WorkConfigureResult, t as WorkContainer, u as WorkCreateField, v as WorkCreateFieldValues, w as WorkCreateInput, x as WorkDescribeContainerInput, y as WorkDescribeContainerResult, z as WorkDisconnectInput, A as WorkDisconnectResult, B as WorkEvent, D as WorkHydrateEventInput, E as WorkHydrateEventResult, F as WorkHydratedCommentCreatedEvent, G as WorkHydratedEvent, H as WorkHydratedEventBase, I as WorkHydratedItemDeletedEvent, J as WorkHydratedStatusChangedEvent, K as WorkHydrationReferenceEvent, N as WorkItemLinkResult, O as WorkItemType, P as WorkLinkedItemInput, Q as WorkListContainersInput, U as WorkListContainersResult, X as WorkMention, Y as WorkMutationInput, Z as WorkOperation, _ as WorkOperationMap, $ as WorkPerson, a0 as WorkRichTextSegment, a1 as WorkSetStatusInput, a2 as WorkSetStatusResult, a3 as WorkStage, a4 as WorkStatus, a5 as createTappifyHandler, a6 as sendWorkEvent } from './work-events-C5n497rx.js';
|
|
7
7
|
import './constants-Bh759FUI.js';
|
|
8
8
|
import 'jose';
|
|
9
9
|
|
package/dist/server.mjs
CHANGED
package/dist/testing/index.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var _chunkVV7ELNRWcjs = require('../chunk-VV7ELNRW.cjs');
|
|
|
30
30
|
require('../chunk-PRCZMOLD.cjs');
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
var
|
|
33
|
+
var _chunkSALMPL5Vcjs = require('../chunk-SALMPL5V.cjs');
|
|
34
34
|
require('../chunk-A74HCO6T.cjs');
|
|
35
35
|
|
|
36
36
|
|
|
@@ -169,7 +169,7 @@ function workItemDeletedFixture(overrides = {}) {
|
|
|
169
169
|
}
|
|
170
170
|
async function signedWorkEventRequest(event, options) {
|
|
171
171
|
let request;
|
|
172
|
-
await
|
|
172
|
+
await _chunkSALMPL5Vcjs.sendWorkEvent.call(void 0, event, {
|
|
173
173
|
...options,
|
|
174
174
|
fetch: (input, init) => {
|
|
175
175
|
request = new Request(input, init);
|
package/dist/testing/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JSONWebKeySet } from 'jose';
|
|
2
2
|
import { a as TapFilters, N as TapProjectResult, o as TapApp, Q as TapRelease, D as TapKeyword, F as TapListing, U as TapReviewsResult, v as TapCrashesResult, R as TapRevenueResult, W as TapSeriesMetric, Y as TapSeriesResult, d as TapQuery, e as TapResult, b as TapSize, T as Tap, a6 as TapToastTone, t as TapConfirmOptions, q as TapChatCard, c as TapHostEvents } from '../types-BdDaLnqs.cjs';
|
|
3
|
-
import { k as TappifyHealth, b as MetricsInput, c as MetricsResponse, M as MentionItems, C as ContextBlock,
|
|
3
|
+
import { k as TappifyHealth, b as MetricsInput, c as MetricsResponse, M as MentionItems, C as ContextBlock, _ as WorkOperationMap, l as TappifyInstall, j as TappifyHandlerOptions, a as TappifyClaims, T as TappifyFetchHandler, B as WorkEvent, S as SendWorkEventOptions, F as WorkHydratedCommentCreatedEvent, I as WorkHydratedItemDeletedEvent, J as WorkHydratedStatusChangedEvent } from '../work-events-DSklu4t-.cjs';
|
|
4
4
|
export { M as MatcherResult, a as MatcherTarget, r as registerTapMatchers, t as tapMatchers } from '../matchers-Cz93AK1n.cjs';
|
|
5
5
|
import { S as ScopeKey, x as MetricDeclaration, a5 as ToolDeclaration } from '../constants-Bh759FUI.cjs';
|
|
6
6
|
import { QueryClient } from '@tanstack/react-query';
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JSONWebKeySet } from 'jose';
|
|
2
2
|
import { a as TapFilters, N as TapProjectResult, o as TapApp, Q as TapRelease, D as TapKeyword, F as TapListing, U as TapReviewsResult, v as TapCrashesResult, R as TapRevenueResult, W as TapSeriesMetric, Y as TapSeriesResult, d as TapQuery, e as TapResult, b as TapSize, T as Tap, a6 as TapToastTone, t as TapConfirmOptions, q as TapChatCard, c as TapHostEvents } from '../types-uGHrRaOj.js';
|
|
3
|
-
import { k as TappifyHealth, b as MetricsInput, c as MetricsResponse, M as MentionItems, C as ContextBlock,
|
|
3
|
+
import { k as TappifyHealth, b as MetricsInput, c as MetricsResponse, M as MentionItems, C as ContextBlock, _ as WorkOperationMap, l as TappifyInstall, j as TappifyHandlerOptions, a as TappifyClaims, T as TappifyFetchHandler, B as WorkEvent, S as SendWorkEventOptions, F as WorkHydratedCommentCreatedEvent, I as WorkHydratedItemDeletedEvent, J as WorkHydratedStatusChangedEvent } from '../work-events-C5n497rx.js';
|
|
4
4
|
export { M as MatcherResult, a as MatcherTarget, r as registerTapMatchers, t as tapMatchers } from '../matchers-Cz93AK1n.js';
|
|
5
5
|
import { S as ScopeKey, x as MetricDeclaration, a5 as ToolDeclaration } from '../constants-Bh759FUI.js';
|
|
6
6
|
import { QueryClient } from '@tanstack/react-query';
|
package/dist/testing/index.mjs
CHANGED
package/dist/vite-preview.cjs
CHANGED
|
@@ -39,7 +39,7 @@ var _jsxruntime = require('react/jsx-runtime'); var JsxRuntime = _interopRequire
|
|
|
39
39
|
// package.json
|
|
40
40
|
var package_default = {
|
|
41
41
|
name: "@tappify/extension-sdk",
|
|
42
|
-
version: "2.1.0-beta.
|
|
42
|
+
version: "2.1.0-beta.3",
|
|
43
43
|
description: "Build extensions that run inside Tappify",
|
|
44
44
|
author: "Tappify Team",
|
|
45
45
|
license: "MIT",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/vite-preview.cjs","../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"],"names":["jsxs","jsx"],"mappings":"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","file":"/home/runner/work/extension-sdk/extension-sdk/dist/vite-preview.cjs","sourcesContent":[null,"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"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/vite-preview.cjs","../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"],"names":["jsxs","jsx"],"mappings":"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","file":"/home/runner/work/extension-sdk/extension-sdk/dist/vite-preview.cjs","sourcesContent":[null,"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.3\",\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"]}
|
package/dist/vite-preview.mjs
CHANGED
|
@@ -39,7 +39,7 @@ import * as JsxRuntime from "react/jsx-runtime";
|
|
|
39
39
|
// package.json
|
|
40
40
|
var package_default = {
|
|
41
41
|
name: "@tappify/extension-sdk",
|
|
42
|
-
version: "2.1.0-beta.
|
|
42
|
+
version: "2.1.0-beta.3",
|
|
43
43
|
description: "Build extensions that run inside Tappify",
|
|
44
44
|
author: "Tappify Team",
|
|
45
45
|
license: "MIT",
|
|
@@ -1 +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"]}
|
|
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.3\",\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":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,kBAAkB;;;ACC3B,YAAY,mBAAmB;AAC/B,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,YAAY,cAAc;AAC1B,YAAY,gBAAgB;;;ACX5B;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,aAAe;AAAA,EACf,QAAU;AAAA,EACV,SAAW;AAAA,EACX,MAAQ;AAAA,EACR,SAAW;AAAA,IACT,KAAK;AAAA,MACH,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,cAAc;AAAA,MACZ,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,YAAY;AAAA,MACV,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,UAAU;AAAA,MACR,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,QAAQ;AAAA,MACN,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,UAAU;AAAA,MACR,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,kBAAkB;AAAA,MAChB,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,4BAA4B;AAAA,MAC1B,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,aAAa;AAAA,MACX,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,oBAAoB;AAAA,MAClB,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,kBAAkB;AAAA,MAChB,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,aAAa;AAAA,MACX,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,mBAAmB;AAAA,IACnB,8BAA8B;AAAA,IAC9B,kBAAkB;AAAA,EACpB;AAAA,EACA,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AAAA,EACA,MAAQ;AAAA,EACR,QAAU;AAAA,EACV,OAAS;AAAA,EACT,aAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,OAAS;AAAA,IACP;AAAA,IACA;AAAA,EACF;AAAA,EACA,YAAc;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,EACT;AAAA,EACA,MAAQ;AAAA,IACN,KAAO;AAAA,EACT;AAAA,EACA,UAAY;AAAA,EACZ,UAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,4BAA4B;AAAA,IAC5B,uBAAuB;AAAA,IACvB,uBAAuB;AAAA,IACvB,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,KAAO;AAAA,IACP,cAAc;AAAA,IACd,QAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,MAAQ;AAAA,IACR,MAAQ;AAAA,IACR,cAAc;AAAA,IACd,SAAW;AAAA,IACX,QAAU;AAAA,IACV,gBAAkB;AAAA,IAClB,YAAc;AAAA,IACd,oBAAoB;AAAA,EACtB;AAAA,EACA,kBAAoB;AAAA,IAClB,2BAA2B;AAAA,IAC3B,yBAAyB;AAAA,IACzB,0BAA0B;AAAA,IAC1B,OAAS;AAAA,IACT,aAAa;AAAA,IACb,MAAQ;AAAA,IACR,QAAU;AAAA,EACZ;AAAA,EACA,sBAAwB;AAAA,IACtB,2BAA2B;AAAA,MACzB,UAAY;AAAA,IACd;AAAA,IACA,yBAAyB;AAAA,MACvB,UAAY;AAAA,IACd;AAAA,IACA,0BAA0B;AAAA,MACxB,UAAY;AAAA,IACd;AAAA,IACA,OAAS;AAAA,MACP,UAAY;AAAA,IACd;AAAA,IACA,aAAa;AAAA,MACX,UAAY;AAAA,IACd;AAAA,IACA,MAAQ;AAAA,MACN,UAAY;AAAA,IACd;AAAA,IACA,QAAU;AAAA,MACR,UAAY;AAAA,IACd;AAAA,EACF;AAAA,EACA,cAAgB;AAAA,IACd,8BAA8B;AAAA,IAC9B,MAAQ;AAAA,IACR,MAAQ;AAAA,IACR,6BAA6B;AAAA,IAC7B,KAAO;AAAA,EACT;AAAA,EACA,iBAAmB;AAAA,IACjB,mBAAmB;AAAA,IACnB,mCAAmC;AAAA,IACnC,cAAc;AAAA,IACd,2BAA2B;AAAA,IAC3B,+BAA+B;AAAA,IAC/B,qCAAqC;AAAA,IACrC,yBAAyB;AAAA,IACzB,4BAA4B;AAAA,IAC5B,yBAAyB;AAAA,IACzB,6CAA6C;AAAA,IAC7C,yBAAyB;AAAA,IACzB,6BAA6B;AAAA,IAC7B,0BAA0B;AAAA,IAC1B,+BAA+B;AAAA,IAC/B,sBAAsB;AAAA,IACtB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,YAAc;AAAA,IACd,8CAA8C;AAAA,IAC9C,QAAU;AAAA,IACV,6BAA6B;AAAA,IAC7B,QAAU;AAAA,IACV,0BAA0B;AAAA,IAC1B,0BAA0B;AAAA,IAC1B,uBAAuB;AAAA,IACvB,6BAA6B;AAAA,IAC7B,SAAW;AAAA,IACX,aAAa;AAAA,IACb,OAAS;AAAA,IACT,eAAe;AAAA,IACf,UAAY;AAAA,IACZ,oCAAoC;AAAA,IACpC,OAAS;AAAA,IACT,aAAa;AAAA,IACb,oBAAoB;AAAA,IACpB,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,SAAW;AAAA,IACX,2BAA2B;AAAA,IAC3B,YAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,MAAQ;AAAA,IACR,QAAU;AAAA,EACZ;AAAA,EACA,QAAU;AAAA,IACR,YAAc;AAAA,MACZ,MAAQ;AAAA,MACR,OAAS;AAAA,QACP,MAAQ;AAAA,UACN,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,KAAO;AAAA,UACL,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,MAAQ;AAAA,UACN,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,OAAS;AAAA,UACP,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,UAAY;AAAA,UACV,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,MAAQ;AAAA,UACN,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,MAAQ;AAAA,UACN,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,OAAS;AAAA,UACP,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,IAAM;AAAA,UACJ,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,OAAS;AAAA,UACP,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,QAAU;AAAA,UACR,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,KAAO;AAAA,UACL,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,MAAQ;AAAA,UACN,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,QACA,SAAW;AAAA,UACT,aAAe;AAAA,UACf,OAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,eAAe;AAAA,MACb;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AC5RA,SAAS,iBAAiD;AA2BlD,SACE,KADF;AAjBD,IAAM,uBAAN,cAAmC,UAGxC;AAAA,EAHK;AAAA;AAIL,iBAAmC,EAAE,OAAO,KAAK;AAAA;AAAA,EAEjD,OAAO,yBAAyB,OAAyC;AACvE,WAAO,EAAE,MAAM;AAAA,EACjB;AAAA,EAEA,kBAAkB,QAAe,OAAwB;AAAA,EAEzD;AAAA,EAEA,SAAoB;AAClB,QAAI,KAAK,MAAM,UAAU,MAAM;AAC7B,aACE,qBAAC,SAAI,WAAU,uBAAsB,MAAK,SACxC;AAAA,4BAAC,YAAO,gDAAkC;AAAA,QAC1C,oBAAC,UAAM,eAAK,MAAM,MAAM,SAAQ;AAAA,SAClC;AAAA,IAEJ;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;;;ACFA,IAAM,YAA2C;AAAA,EAC/C,MAAM;AAAA,EACN,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,UAAU;AACZ;AAEA,SAAS,cAAc,UAAkD;AACvE,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,cAAc,SAAS;AAE7B,aAAW,QAAQ,YAAY,SAAS,CAAC,GAAG;AAC1C,WAAO,IAAI,WAAW,KAAK,EAAE,IAAI,KAAK,KAAK;AAAA,EAC7C;AACA,aAAW,OAAO,YAAY,QAAQ,CAAC,GAAG;AACxC,WAAO,IAAI,UAAU,IAAI,EAAE,IAAI,IAAI,KAAK;AAAA,EAC1C;AACA,aAAW,UAAU,YAAY,WAAW,CAAC,GAAG;AAC9C,WAAO,IAAI,aAAa,OAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EACnD;AACA,aAAW,UAAU,YAAY,cAAc,CAAC,GAAG;AACjD,WAAO,IAAI,gBAAgB,OAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EACtD;AACA,MAAI,YAAY,SAAU,QAAO,IAAI,cAAc,UAAU;AAE7D,SAAO;AACT;AAEO,SAAS,kBACd,UACoB;AACpB,QAAM,SAAS,cAAc,QAAQ;AACrC,SAAO,gBAAgB,QAAQ,EAAE,IAAI,kBAAgB;AACnD,UAAM,QAAQ,OAAO,IAAI,aAAa,MAAM,KAAK,aAAa;AAC9D,WAAO;AAAA,MACL;AAAA,MACA,OACE,aAAa,SAAS,aAClB,QACA,GAAG,KAAK,KAAK,UAAU,aAAa,IAAI,CAAC;AAAA,IACjD;AAAA,EACF,CAAC;AACH;AAEO,SAAS,aACd,cACA,OACc;AACd,UAAQ,aAAa,MAAM;AAAA,IACzB,KAAK;AACH,aAAO,EAAE,QAAQ,EAAE,QAAQ,aAAa,IAAI,MAAM,IAAI,EAAE;AAAA,IAC1D,KAAK;AACH,aAAO,CAAC;AAAA,IACV,KAAK;AACH,aAAO,EAAE,QAAQ,CAAC,EAAE;AAAA,IACtB,KAAK;AACH,aAAO,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAC,EAAE,EAAE;AAAA,IAC1C,KAAK;AACH,aAAO;AAAA,QACL,QAAQ,MAAM;AAAA,QACd,UAAU,YAAU,MAAM,iBAAiB,MAAM;AAAA,MACnD;AAAA,EACJ;AACF;;;ACjGO,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AJ0LzB,SACE,OAAAA,MADF,QAAAC,aAAA;AA/HJ,IAAM,YAAY,EAAE,WAAW,MAAM,iBAAiB,MAAM;AAC5D,IAAM,sBAAsB,oBAAI,IAAuC;AACvE,IAAI,qBAAqB;AAEzB,eAAe,cACb,aACA,QAC2B;AAC3B,QAAM,MAAM,GAAG,WAAW,IAAI,MAAM;AACpC,QAAM,WAAW,oBAAoB,IAAI,GAAG;AAC5C,MAAI,aAAa,OAAW,QAAO;AAEnC,wBAAsB;AACtB,QAAM,OAAO,WAAW,WAAW;AACnC,QAAM,UAAU,OAAO,4BAA4B,EAAE;AAAA,IACnD,CAAC,EAAE,eAAe,MAChB,eAAe;AAAA,MACb,MAAM,yBAAyB,OAAO,kBAAkB,CAAC;AAAA,MACzD,SAAS,CAAC,EAAE,MAAM,OAAO,MAAM,OAAO,QAAQ,MAAM,SAAS,CAAC;AAAA,MAC9D,QAAQ;AAAA,QACN,OAAO;AAAA,UACL,SAAe;AAAA,UACf,KAAK,MAAM;AAAA,UACX,aAAa;AAAA,QACf;AAAA,QACA,aAAa;AAAA,UACX,SAAkB;AAAA,UAClB,KAAK,MAAM;AAAA,UACX,aAAa;AAAA,QACf;AAAA,QACA,qBAAqB;AAAA,UACnB,SAAe;AAAA,UACf,KAAK,MAAM;AAAA,UACX,aAAa;AAAA,QACf;AAAA,QACA,yBAAyB;AAAA,UACvB,SAAS,gBAAY,iBACnB,uBACF,EAAE,QAAQ,WAAW,EAAE;AAAA,UACvB,KAAK,MAAM;AAAA,UACX,aAAa;AAAA,QACf;AAAA,QACA,0BAA0B;AAAA,UACxB,SAAS,gBAAY;AAAA,UACrB,KAAK,MAAM;AAAA,UACX,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACL;AACA,sBAAoB,IAAI,KAAK,OAAO;AACpC,SAAO;AACT;AAEO,SAAS,oBACd,QACA,QACwC;AACxC,QAAM,YACJ,OAAO,WAAW,YAAY,WAAW,QAAQ,aAAa,SACzD,OAAgC,UACjC;AACN,MAAI,OAAO,cAAc,YAAY;AACnC,UAAM,IAAI;AAAA,MACR,GAAG,MAAM;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,QAA2B;AACnD,MACE,OAAO,WAAW,YAClB,WAAW,QACX,EAAE,YAAY,WACd,CAAC,MAAM,QAAQ,OAAO,MAAM,GAC5B;AACA,WAAO,CAAC;AAAA,EACV;AACA,SAAO,OAAO,OAAO;AAAA,IACnB,CAAC,UAA2B,OAAO,UAAU;AAAA,EAC/C;AACF;AAEA,eAAsB,iBAAiB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF,GAAyC;AACvC,QAAM,aAAa,MAAM,cAAc,aAAa,MAAM;AAC1D,SAAO,WAAW;AAAA,IAChB,GAAG,WAAW,WAAW,CAAC,IAAI,OAAO,QAAQ,SAAS,EAAE,CAAC;AAAA,EAC3D;AACF;AAEA,SAAS,iBAAiB,QAAyB;AACjD,MAAI;AACF,UAAM,OAAO,IAAI,IAAI,MAAM,EAAE;AAC7B,WAAO,SAAS,eAAe,SAAS,eAAe,SAAS;AAAA,EAClE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBACP,UAC6B;AAC7B,SAAO,OAAO;AAAA,IACZ,OAAO,KAAK,SAAS,QAAQ,cAAc,CAAC,CAAC,EAAE,IAAI,UAAQ;AAAA,MACzD;AAAA,MACA,MAAM;AACJ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,SAAS,QAAQ;AAAA,EACf;AAAA,EACA;AACF,GAGG;AACD,SACE,gBAAAA,MAAC,SAAI,WAAU,uBAAsB,MAAK,UACxC;AAAA,oBAAAD,KAAC,QAAI,iBAAM;AAAA,IACX,gBAAAA,KAAC,UAAM,UAAS;AAAA,KAClB;AAEJ;AAOA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAoC,IAAI;AAClE,QAAM,aAAa,YAAY,CAAC,SAAsC;AACpE,QAAI,SAAS,KAAM;AACnB,UAAM,OAAO,KAAK,cAAc,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAClE,QAAI,UAAU,KAAK,cAA2B,oBAAoB;AAClE,QAAI,YAAY,MAAM;AACpB,gBAAU,SAAS,cAAc,KAAK;AACtC,cAAQ,YAAY;AACpB,cAAQ,aAAa,oBAAoB,EAAE;AAC3C,WAAK,YAAY,OAAO;AAAA,IAC1B;AACA,aAAS,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC5B,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,QAAI,UAAU,KAAM;AACpB,UAAM,KAAK,YAAY,MAAM;AAC7B,WAAO,MAAM,OAAO,OAAO;AAAA,EAC7B,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,YAAU,MAAM;AACd,QAAI,UAAU,KAAM;AACpB,UAAM,WAA+B,CAAC;AACtC,UAAM,SAAS,CAAC,MAAc,QAAsB;AAClD,YAAM,UAAU,SAAS,cAAc,OAAO;AAC9C,cAAQ,aAAa,kBAAkB,IAAI;AAC3C,cAAQ,cAAc;AACtB,YAAM,KAAK,aAAa,SAAS,MAAM,OAAO;AAC9C,eAAS,KAAK,OAAO;AAAA,IACvB;AAEA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AACA,eAAW,OAAO,OAAQ,QAAO,UAAU,GAAG;AAC9C,WAAO,MAAM;AACX,iBAAW,WAAW,SAAU,SAAQ,OAAO;AAAA,IACjD;AAAA,EACF,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV,4BAAwB;AAAA,MAEvB,oBAAU,OAAO,OAAgB,sBAAa,UAAU,MAAM,OAAO;AAAA;AAAA,EACxE;AAEJ;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,SAAS;AACX,GAAsB;AACpB,QAAM,aAAa,QAAQ,MAAM,kBAAkB,QAAQ,GAAG,CAAC,QAAQ,CAAC;AACxE,QAAM,CAAC,QAAQ,SAAS,IAAI;AAAA,IAC1B,WAAW,CAAC,GAAG,aAAa,UAAU;AAAA,EACxC;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAgB,OAAO;AACjD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAmB,SAAS;AAC5D,QAAM,CAAC,MAAM,OAAO,IAAI,SAAkB,MAAM;AAChD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,CAAC;AAC9C,QAAM,CAAC,UAAU,WAAW,IAAI,SAA4B,CAAC,CAAC;AAC9D,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAoB;AAAA,IAC9C,KAAK;AAAA,IACL,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,YACJ,WAAW,KAAK,UAAQ,KAAK,aAAa,WAAW,MAAM,KAC3D,WAAW,CAAC;AACd,QAAM,eAAe,WAAW;AAChC,QAAM,UAAU,GAAG,MAAM,IAAI,cAAc,UAAU,EAAE;AAEvD,YAAU,MAAM;AACd,QAAI,iBAAiB,OAAW;AAChC,QAAI,YAAY;AAChB,cAAU,EAAE,KAAK,SAAS,QAAQ,UAAU,CAAC;AAC7C,SAAK,OAAO;AAAA,MACV,aAAa,SAAS;AAAA,MACtB;AAAA,MACA,QAAQ,aAAa;AAAA,IACvB,CAAC,EACE,KAAK,YAAU;AACd,UAAI,UAAW;AACf,gBAAU;AAAA,QACR,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,WAAW,oBAAoB,QAAQ,aAAa,MAAM;AAAA,QAC1D,QAAQ,iBAAiB,MAAM;AAAA,MACjC,CAAC;AAAA,IACH,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAI,UAAW;AACf,gBAAU;AAAA,QACR,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,SACE,iBAAiB,QACb,MAAM,UACN;AAAA,MACR,CAAC;AAAA,IACH,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,cAAc,SAAS,QAAQ,SAAS,IAAI,MAAM,CAAC;AAEvD,QAAM,OAAO,QAAQ,MAAM;AACzB,SAAK;AACL,WAAO,cAAc;AAAA,MACnB,aAAa,SAAS;AAAA,MACtB,eAAe,SAAS;AAAA,MACxB,SAAS,SAAS,QAAQ;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,QAAQ,oBAAoB,QAAQ;AAAA,IACtC,CAAC;AAAA,EACH,GAAG,CAAC,YAAY,UAAU,MAAM,KAAK,CAAC;AACtC,QAAM,cAAc,QAAQ,MAAM;AAChC,SAAK;AACL,WAAO,sBAAsB;AAAA,EAC/B,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,UACJ,OAAO,QAAQ,UAAU,SAAS,EAAE,KAAK,SAAS,QAAQ,UAAU;AAEtE,MAAI;AACJ,MAAI,iBAAiB,QAAW;AAC9B,cACE,gBAAAA,KAAC,WAAQ,OAAM,uBAAsB,wGAGrC;AAAA,EAEJ,WAAW,QAAQ,WAAW,UAAU;AACtC,cACE,gBAAAC,MAAC,SAAI,WAAU,uBAAsB,MAAK,SACxC;AAAA,sBAAAD,KAAC,YAAO,8CAAgC;AAAA,MACxC,gBAAAA,KAAC,UAAM,kBAAQ,SAAQ;AAAA,OACzB;AAAA,EAEJ,WAAW,QAAQ,WAAW,SAAS;AACrC,UAAME,aAAY,QAAQ;AAC1B,UAAM,QAAQ,aAAa,cAAc;AAAA,MACvC;AAAA,MACA,kBAAkB;AAAA,IACpB,CAAC;AACD,cACE,gBAAAF,KAAC,mBAAgB,KAAK,KAAK,KAAK,aAC9B,0BAAAA,KAAC,kBAAe,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,QACnD,0BAAAA,KAAC,wBACC,0BAAAA,KAACE,YAAA,EAAW,GAAG,OAAO,KADG,GAAG,OAAO,IAAI,OAAO,UAAU,CAAC,EAE3D,GACF,GACF;AAAA,EAEJ,OAAO;AACL,cACE,gBAAAD,MAAC,WAAQ,OAAM,wBAAuB;AAAA;AAAA,MAAS,UAAU;AAAA,MAAM;AAAA,OAAC;AAAA,EAEpE;AAEA,SACE,gBAAAA,MAAC,UAAK,WAAU,eAAc,cAAY,OACxC;AAAA,oBAAAD,KAAC,WAAO,yBAAc;AAAA,IACtB,gBAAAC,MAAC,YAAO,WAAU,mBAChB;AAAA,sBAAAA,MAAC,SAAI,WAAU,qBACb;AAAA,wBAAAD,KAAC,SAAI,KAAK,IAAI,SAAS,KAAK,QAAQ,SAAS,EAAE,CAAC,IAAI,KAAI,IAAG;AAAA,QAC3D,gBAAAC,MAAC,SACC;AAAA,0BAAAD,KAAC,YAAQ,mBAAS,MAAK;AAAA,UACvB,gBAAAC,MAAC,UACC;AAAA,4BAAAD,KAAC,OAAE,eAAY,QAAO;AAAA,YACrB,iBAAiB,MAAM,IAAI,oBAAoB;AAAA,YAC/C;AAAA,aACH;AAAA,WACF;AAAA,SACF;AAAA,MACA,gBAAAC,MAAC,SAAI,WAAU,wBACZ;AAAA,mBAAW,SAAS,IACnB,gBAAAA,MAAC,WAAM,WAAU,qDACf;AAAA,0BAAAD,KAAC,UAAK,0BAAY;AAAA,UAClB,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,cAAc,UAAU;AAAA,cAC/B,UAAU,WAAS,UAAU,MAAM,OAAO,KAAK;AAAA,cAE9C,qBAAW,IAAI,UACd,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBAEC,OAAO,KAAK,aAAa;AAAA,kBAExB,eAAK;AAAA;AAAA,gBAHD,KAAK,aAAa;AAAA,cAIzB,CACD;AAAA;AAAA,UACH;AAAA,WACF,IACE;AAAA,QACJ,gBAAAC,MAAC,WAAM,WAAU,qBACf;AAAA,0BAAAD,KAAC,UAAK,mBAAK;AAAA,UACX,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,WAAS,SAAS,MAAM,OAAO,KAAc;AAAA,cAEvD;AAAA,gCAAAD,KAAC,YAAO,OAAM,SAAQ,mBAAK;AAAA,gBAC3B,gBAAAA,KAAC,YAAO,OAAM,QAAO,kBAAI;AAAA;AAAA;AAAA,UAC3B;AAAA,WACF;AAAA,QACA,gBAAAC,MAAC,WAAM,WAAU,qBACf;AAAA,0BAAAD,KAAC,UAAK,sBAAQ;AAAA,UACd,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,WAAS,YAAY,MAAM,OAAO,KAAiB;AAAA,cAE7D;AAAA,gCAAAD,KAAC,YAAO,OAAM,WAAU,qBAAO;AAAA,gBAC/B,gBAAAA,KAAC,YAAO,OAAM,UAAS,oBAAM;AAAA,gBAC7B,gBAAAA,KAAC,YAAO,OAAM,UAAS,oBAAM;AAAA;AAAA;AAAA,UAC/B;AAAA,WACF;AAAA,QACA,gBAAAC,MAAC,WAAM,WAAU,qBACf;AAAA,0BAAAD,KAAC,UAAK,qBAAO;AAAA,UACb,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,cAAW;AAAA,cACX,UAAU,WAAS,QAAQ,MAAM,OAAO,KAAgB;AAAA,cAExD;AAAA,gCAAAD,KAAC,YAAO,OAAM,QAAO,kBAAI;AAAA,gBACzB,gBAAAA,KAAC,YAAO,OAAM,SAAQ,mBAAK;AAAA,gBAC3B,gBAAAA,KAAC,YAAO,OAAM,QAAO,kBAAI;AAAA;AAAA;AAAA,UAC3B;AAAA,WACF;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,MAAK;AAAA,YACL,cAAW;AAAA,YACX,SAAS,MAAM;AACb,0BAAY,CAAC,CAAC;AACd,4BAAc,WAAS,QAAQ,CAAC;AAAA,YAClC;AAAA,YACD;AAAA;AAAA,QAED;AAAA,SACF;AAAA,OACF;AAAA,IACA,gBAAAA,KAAC,aAAQ,WAAU,qBACjB,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,iBAAe;AAAA,QACf,aAAW;AAAA,QACX,cAAW;AAAA,QAEV;AAAA;AAAA,IACH,GACF;AAAA,KACF;AAEJ;;;AD5bc,gBAAAG,YAAA;AANP,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AACF,GAAoC;AAClC,QAAM,OAAO,WAAW,KAAK;AAC7B,OAAK,OAAO,gBAAAA,KAAC,gBAAa,UAAoB,QAAgB,CAAE;AAChE,SAAO,MAAM,KAAK,QAAQ;AAC5B;","names":["jsx","jsxs","Component","jsx"]}
|
|
@@ -6,6 +6,25 @@ import { a9 as WorkContribution } from './constants-Bh759FUI.js';
|
|
|
6
6
|
type WorkStage = keyof NonNullable<NonNullable<WorkContribution['status']>['map']>;
|
|
7
7
|
/** JSON-safe values supplied for provider create fields. */
|
|
8
8
|
type WorkCreateFieldValues = Record<string, string | number | boolean | string[] | null>;
|
|
9
|
+
/** A person in the sending system; email is optional when unavailable. */
|
|
10
|
+
interface WorkPerson {
|
|
11
|
+
id: string;
|
|
12
|
+
displayName: string;
|
|
13
|
+
email?: string;
|
|
14
|
+
}
|
|
15
|
+
/** A structured person mention supplied with a Review comment. */
|
|
16
|
+
interface WorkMention {
|
|
17
|
+
person: WorkPerson;
|
|
18
|
+
}
|
|
19
|
+
/** Ordered provider comment content, including portable person mentions. */
|
|
20
|
+
type WorkRichTextSegment = {
|
|
21
|
+
type: 'text';
|
|
22
|
+
text: string;
|
|
23
|
+
} | {
|
|
24
|
+
type: 'user_mention';
|
|
25
|
+
person: WorkPerson;
|
|
26
|
+
fallback: string;
|
|
27
|
+
};
|
|
9
28
|
/** Logical mutation identity, stable across transport retries. */
|
|
10
29
|
interface WorkMutationInput {
|
|
11
30
|
idempotencyKey: string;
|
|
@@ -122,6 +141,9 @@ interface ReviewThreadSource {
|
|
|
122
141
|
firstComment: {
|
|
123
142
|
id: string;
|
|
124
143
|
body: string;
|
|
144
|
+
author: WorkPerson;
|
|
145
|
+
mentions: WorkMention[];
|
|
146
|
+
/** @deprecated Use author.displayName instead. */
|
|
125
147
|
authorDisplayName: string;
|
|
126
148
|
createdAt: string;
|
|
127
149
|
};
|
|
@@ -164,6 +186,9 @@ interface WorkAttachResult {
|
|
|
164
186
|
interface WorkCommentInput extends WorkMutationInput, WorkLinkedItemInput {
|
|
165
187
|
commentId: string;
|
|
166
188
|
body: string;
|
|
189
|
+
author: WorkPerson;
|
|
190
|
+
mentions: WorkMention[];
|
|
191
|
+
/** @deprecated Use author.displayName instead. */
|
|
167
192
|
authorDisplayName: string;
|
|
168
193
|
createdAt: string;
|
|
169
194
|
}
|
|
@@ -198,13 +223,20 @@ interface WorkHydratedCommentCreatedEvent extends WorkHydratedEventBase {
|
|
|
198
223
|
type: 'comment.created';
|
|
199
224
|
comment: {
|
|
200
225
|
externalCommentId: string;
|
|
226
|
+
/** Compatibility text; segment concatenation is authoritative when present. */
|
|
201
227
|
body: string;
|
|
202
|
-
author: {
|
|
203
|
-
id: string;
|
|
204
|
-
displayName: string;
|
|
205
|
-
};
|
|
206
228
|
createdAt: string;
|
|
207
|
-
}
|
|
229
|
+
} & ({
|
|
230
|
+
author: WorkPerson;
|
|
231
|
+
/** Concatenate text and mention fallback values in order. */
|
|
232
|
+
segments: WorkRichTextSegment[];
|
|
233
|
+
} | {
|
|
234
|
+
/** Legacy authors cannot carry rich person data without segments. */
|
|
235
|
+
author: WorkPerson & {
|
|
236
|
+
email?: never;
|
|
237
|
+
};
|
|
238
|
+
segments?: never;
|
|
239
|
+
});
|
|
208
240
|
}
|
|
209
241
|
/** A complete provider status-change signal. */
|
|
210
242
|
interface WorkHydratedStatusChangedEvent extends WorkHydratedEventBase {
|
|
@@ -544,4 +576,4 @@ interface SendWorkEventOptions {
|
|
|
544
576
|
*/
|
|
545
577
|
declare function sendWorkEvent(event: WorkEvent, options: SendWorkEventOptions): Promise<void>;
|
|
546
578
|
|
|
547
|
-
export { type
|
|
579
|
+
export { type WorkPerson as $, type WorkDisconnectResult as A, type WorkEvent as B, type ContextBlock as C, type WorkHydrateEventInput as D, type WorkHydrateEventResult as E, type WorkHydratedCommentCreatedEvent as F, type WorkHydratedEvent as G, type WorkHydratedEventBase as H, type WorkHydratedItemDeletedEvent as I, type WorkHydratedStatusChangedEvent as J, type WorkHydrationReferenceEvent as K, type LegacyWorkHandlers as L, type MentionItems as M, type WorkItemLinkResult as N, type WorkItemType as O, type WorkLinkedItemInput as P, type WorkListContainersInput as Q, type ReviewAttachmentDescriptor as R, type SendWorkEventOptions as S, type TappifyFetchHandler as T, type WorkListContainersResult as U, type VerifyTokenOptions as V, type WorkAttachInput as W, type WorkMention as X, type WorkMutationInput as Y, type WorkOperation as Z, type WorkOperationMap as _, type TappifyClaims as a, type WorkRichTextSegment as a0, type WorkSetStatusInput as a1, type WorkSetStatusResult as a2, type WorkStage as a3, type WorkStatus as a4, createTappifyHandler as a5, sendWorkEvent as a6, type MetricsInput as b, type MetricsResponse as c, type MetricsSeries as d, type ReviewImageAttachmentDescriptor as e, type ReviewLinkAttachmentDescriptor as f, type ReviewThreadSource as g, type TappifyCallContext as h, type TappifyDocuments as i, type TappifyHandlerOptions as j, type TappifyHealth as k, type TappifyInstall as l, type TappifyRequest as m, type TappifyRouteHandler as n, type WorkAttachResult as o, type WorkCommentInput as p, type WorkCommentResult as q, type WorkConfigureInput as r, type WorkConfigureResult as s, type WorkContainer as t, type WorkCreateField as u, type WorkCreateFieldValues as v, type WorkCreateInput as w, type WorkDescribeContainerInput as x, type WorkDescribeContainerResult as y, type WorkDisconnectInput as z };
|
|
@@ -6,6 +6,25 @@ import { a9 as WorkContribution } from './constants-Bh759FUI.cjs';
|
|
|
6
6
|
type WorkStage = keyof NonNullable<NonNullable<WorkContribution['status']>['map']>;
|
|
7
7
|
/** JSON-safe values supplied for provider create fields. */
|
|
8
8
|
type WorkCreateFieldValues = Record<string, string | number | boolean | string[] | null>;
|
|
9
|
+
/** A person in the sending system; email is optional when unavailable. */
|
|
10
|
+
interface WorkPerson {
|
|
11
|
+
id: string;
|
|
12
|
+
displayName: string;
|
|
13
|
+
email?: string;
|
|
14
|
+
}
|
|
15
|
+
/** A structured person mention supplied with a Review comment. */
|
|
16
|
+
interface WorkMention {
|
|
17
|
+
person: WorkPerson;
|
|
18
|
+
}
|
|
19
|
+
/** Ordered provider comment content, including portable person mentions. */
|
|
20
|
+
type WorkRichTextSegment = {
|
|
21
|
+
type: 'text';
|
|
22
|
+
text: string;
|
|
23
|
+
} | {
|
|
24
|
+
type: 'user_mention';
|
|
25
|
+
person: WorkPerson;
|
|
26
|
+
fallback: string;
|
|
27
|
+
};
|
|
9
28
|
/** Logical mutation identity, stable across transport retries. */
|
|
10
29
|
interface WorkMutationInput {
|
|
11
30
|
idempotencyKey: string;
|
|
@@ -122,6 +141,9 @@ interface ReviewThreadSource {
|
|
|
122
141
|
firstComment: {
|
|
123
142
|
id: string;
|
|
124
143
|
body: string;
|
|
144
|
+
author: WorkPerson;
|
|
145
|
+
mentions: WorkMention[];
|
|
146
|
+
/** @deprecated Use author.displayName instead. */
|
|
125
147
|
authorDisplayName: string;
|
|
126
148
|
createdAt: string;
|
|
127
149
|
};
|
|
@@ -164,6 +186,9 @@ interface WorkAttachResult {
|
|
|
164
186
|
interface WorkCommentInput extends WorkMutationInput, WorkLinkedItemInput {
|
|
165
187
|
commentId: string;
|
|
166
188
|
body: string;
|
|
189
|
+
author: WorkPerson;
|
|
190
|
+
mentions: WorkMention[];
|
|
191
|
+
/** @deprecated Use author.displayName instead. */
|
|
167
192
|
authorDisplayName: string;
|
|
168
193
|
createdAt: string;
|
|
169
194
|
}
|
|
@@ -198,13 +223,20 @@ interface WorkHydratedCommentCreatedEvent extends WorkHydratedEventBase {
|
|
|
198
223
|
type: 'comment.created';
|
|
199
224
|
comment: {
|
|
200
225
|
externalCommentId: string;
|
|
226
|
+
/** Compatibility text; segment concatenation is authoritative when present. */
|
|
201
227
|
body: string;
|
|
202
|
-
author: {
|
|
203
|
-
id: string;
|
|
204
|
-
displayName: string;
|
|
205
|
-
};
|
|
206
228
|
createdAt: string;
|
|
207
|
-
}
|
|
229
|
+
} & ({
|
|
230
|
+
author: WorkPerson;
|
|
231
|
+
/** Concatenate text and mention fallback values in order. */
|
|
232
|
+
segments: WorkRichTextSegment[];
|
|
233
|
+
} | {
|
|
234
|
+
/** Legacy authors cannot carry rich person data without segments. */
|
|
235
|
+
author: WorkPerson & {
|
|
236
|
+
email?: never;
|
|
237
|
+
};
|
|
238
|
+
segments?: never;
|
|
239
|
+
});
|
|
208
240
|
}
|
|
209
241
|
/** A complete provider status-change signal. */
|
|
210
242
|
interface WorkHydratedStatusChangedEvent extends WorkHydratedEventBase {
|
|
@@ -544,4 +576,4 @@ interface SendWorkEventOptions {
|
|
|
544
576
|
*/
|
|
545
577
|
declare function sendWorkEvent(event: WorkEvent, options: SendWorkEventOptions): Promise<void>;
|
|
546
578
|
|
|
547
|
-
export { type
|
|
579
|
+
export { type WorkPerson as $, type WorkDisconnectResult as A, type WorkEvent as B, type ContextBlock as C, type WorkHydrateEventInput as D, type WorkHydrateEventResult as E, type WorkHydratedCommentCreatedEvent as F, type WorkHydratedEvent as G, type WorkHydratedEventBase as H, type WorkHydratedItemDeletedEvent as I, type WorkHydratedStatusChangedEvent as J, type WorkHydrationReferenceEvent as K, type LegacyWorkHandlers as L, type MentionItems as M, type WorkItemLinkResult as N, type WorkItemType as O, type WorkLinkedItemInput as P, type WorkListContainersInput as Q, type ReviewAttachmentDescriptor as R, type SendWorkEventOptions as S, type TappifyFetchHandler as T, type WorkListContainersResult as U, type VerifyTokenOptions as V, type WorkAttachInput as W, type WorkMention as X, type WorkMutationInput as Y, type WorkOperation as Z, type WorkOperationMap as _, type TappifyClaims as a, type WorkRichTextSegment as a0, type WorkSetStatusInput as a1, type WorkSetStatusResult as a2, type WorkStage as a3, type WorkStatus as a4, createTappifyHandler as a5, sendWorkEvent as a6, type MetricsInput as b, type MetricsResponse as c, type MetricsSeries as d, type ReviewImageAttachmentDescriptor as e, type ReviewLinkAttachmentDescriptor as f, type ReviewThreadSource as g, type TappifyCallContext as h, type TappifyDocuments as i, type TappifyHandlerOptions as j, type TappifyHealth as k, type TappifyInstall as l, type TappifyRequest as m, type TappifyRouteHandler as n, type WorkAttachResult as o, type WorkCommentInput as p, type WorkCommentResult as q, type WorkConfigureInput as r, type WorkConfigureResult as s, type WorkContainer as t, type WorkCreateField as u, type WorkCreateFieldValues as v, type WorkCreateInput as w, type WorkDescribeContainerInput as x, type WorkDescribeContainerResult as y, type WorkDisconnectInput as z };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/chunk-EB5XWQAU.cjs","../src/server/events.ts","../src/server/work-events.ts"],"names":[],"mappings":"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","file":"/home/runner/work/extension-sdk/extension-sdk/dist/chunk-EB5XWQAU.cjs","sourcesContent":[null,"import { TapServerError } from '../client/errors';\nimport type { WebhookName, WebhookPayload } from '../client/types';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { readRecord, readText } from './wire';\n\n/** The body `sendEvent` posts to Tappify, and the body the signature covers. */\nexport interface WebhookEnvelope {\n /** The declared webhook name. */\n event: string;\n /** The extension id the event came from. */\n source: string;\n /** When the event happened, as an ISO timestamp. */\n occurredAt: string;\n /** The build the event belongs to, when it belongs to one. */\n build?: string;\n /** The key Tappify deduplicates on, so a retry is not counted twice. */\n dedupeKey: string;\n payload: Record<string, unknown>;\n}\n\n/** The third argument of `sendEvent`. */\nexport interface SendEventOptions {\n /** Which install the event belongs to. */\n installId: string;\n extensionId: string;\n /** The inbound signing secret from the extension's Server page. */\n secret: string;\n /** A full url to post to, which replaces `baseUrl` and the built path. */\n endpoint?: string;\n /** The Tappify origin to build the path under. Defaults to production. */\n baseUrl?: string;\n /** Defaults to a fresh UUID, so give your own to make a retry idempotent. */\n dedupeKey?: string;\n build?: string;\n /** Defaults to now, in ISO form. */\n occurredAt?: string;\n /** A `fetch` of your own, for a test or a proxy. Defaults to the global one. */\n fetch?: typeof fetch;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.tappify.ai';\n\nfunction toHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map(byte => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Returns the hex HMAC-SHA-256 of a raw body under a shared secret.\n *\n * @remarks\n * This is the value `sendEvent` sends as `X-Tappify-Signature`, computed over the\n * exact bytes of the body it posts. Call it directly only when you build the\n * request yourself, and sign the serialised string you send rather than\n * re-serialising the object, because a different key order is a different\n * signature. Uses Web Crypto, so it runs on Node, Cloudflare Workers and Vercel\n * alike.\n *\n * @example\n * ```ts\n * import { signWebhook } from '@tappify/extension-sdk/server';\n *\n * async function signed(body: string): Promise<Record<string, string>> {\n * return { 'x-tappify-signature': await signWebhook('whsec_test', body) };\n * }\n * ```\n */\nexport async function signWebhook(\n secret: string,\n body: string,\n): Promise<string> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(body));\n return toHex(signature);\n}\n\nasync function rejectionCode(response: Response): Promise<string | null> {\n try {\n const payload: unknown = await response.json();\n return readText(readRecord(payload, 'error'), 'code');\n } catch {\n return null;\n }\n}\n\n/**\n * Posts one of your declared webhook events to Tappify, signed with the install's\n * inbound secret.\n *\n * @remarks\n * The name and the payload are narrowed by the `TapWebhookMap` augmentation\n * `tappify extension types` writes. It builds the envelope, signs the serialised\n * body and posts it to the install's hook path under `baseUrl`, or to `endpoint`\n * when you give one. A response Tappify did not accept rejects with a\n * `TapServerError` whose `code` is always `TAP_WEBHOOK_REJECTED`, carrying the HTTP\n * status, and with Tappify's own code in the message text rather than in `code`;\n * the two usual causes are the wrong signing\n * secret and an event the manifest does not declare. Pass your own `dedupeKey` to\n * make a retry idempotent, because the default is a fresh UUID.\n *\n * @example\n * ```ts\n * import { sendEvent } from '@tappify/extension-sdk/server';\n *\n * async function reportAnomaly(installId: string, why: string): Promise<void> {\n * await sendEvent('anomaly.detected', { why }, {\n * installId,\n * extensionId: 'starter',\n * secret: process.env.TAPPIFY_INBOUND_SECRET ?? '',\n * dedupeKey: `anomaly-${installId}-${why}`,\n * });\n * }\n * ```\n */\nexport async function sendEvent<E extends WebhookName>(\n name: E,\n payload: WebhookPayload<E>,\n options: SendEventOptions,\n): Promise<void> {\n const envelope: WebhookEnvelope = {\n event: name,\n source: options.extensionId,\n occurredAt: options.occurredAt ?? new Date().toISOString(),\n dedupeKey: options.dedupeKey ?? crypto.randomUUID(),\n payload: { ...payload },\n };\n\n if (options.build !== undefined) envelope.build = options.build;\n\n const body = JSON.stringify(envelope);\n const signature = await signWebhook(options.secret, body);\n const url =\n options.endpoint ??\n `${options.baseUrl ?? DEFAULT_BASE_URL}/api/v1/extensions/hooks/${options.extensionId}/${options.installId}`;\n\n const send = options.fetch ?? fetch;\n const response = await send(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n const rejection = await rejectionCode(response);\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for ${name}. Check the inbound signing secret on the extension's Server page, and that the event is declared under contributes.webhooks.`,\n response.status,\n );\n }\n}\n","import { TapServerError } from '../client/errors';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { signWebhook } from './events';\nimport { readRecord, readText, readUnknown } from './wire';\nimport type { WorkHydratedEvent, WorkHydratedEventBase } from './work';\n\n/** An opaque provider signal to hydrate using current install credentials. */\nexport interface WorkHydrationReferenceEvent extends WorkHydratedEventBase {\n type: WorkHydratedEvent['type'];\n /** A nonempty, short-lived reference resolved by the hydrate_event handler. */\n hydrateRef: string;\n /** Normalized comment content must not accompany a reference. */\n comment?: never;\n /** Normalized status content must not accompany a reference. */\n status?: never;\n}\n\n/** Complete normalized content or an opaque reference, with stable retry identity. */\nexport type WorkEvent =\n | (WorkHydratedEvent & { hydrateRef?: never })\n | WorkHydrationReferenceEvent;\n\n/** Install-specific delivery values provisioned through work configure. */\nexport interface SendWorkEventOptions {\n /** The exact provisioned callback URL; no production default is supplied. */\n callbackUrl: string;\n /** The per-install work-event signing secret provisioned with the callback. */\n secret: string;\n /** A transport for tests or a proxy; defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nfunction nonempty(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction validEvent(event: WorkEvent): boolean {\n const type = readText(event, 'type');\n if (\n !['comment.created', 'status.changed', 'item.deleted'].includes(type ?? '')\n )\n return false;\n if (\n !['externalItemId', 'providerEventId', 'occurredAt', 'dedupeKey'].every(\n key => nonempty(readUnknown(event, key)),\n )\n )\n return false;\n\n const reference = readUnknown(event, 'hydrateRef');\n const comment = readUnknown(event, 'comment');\n const status = readUnknown(event, 'status');\n if (reference !== undefined)\n return nonempty(reference) && comment === undefined && status === undefined;\n\n if (type === 'item.deleted')\n return comment === undefined && status === undefined;\n if (type === 'status.changed') {\n const normalized = readRecord(event, 'status');\n return (\n comment === undefined &&\n nonempty(normalized.id) &&\n nonempty(normalized.label) &&\n typeof normalized.stage === 'string' &&\n ['open', 'in_progress', 'done'].includes(normalized.stage)\n );\n }\n\n const normalized = readRecord(event, 'comment');\n const author = readRecord(normalized, 'author');\n return (\n status === undefined &&\n nonempty(normalized.externalCommentId) &&\n typeof normalized.body === 'string' &&\n nonempty(normalized.createdAt) &&\n nonempty(author.id) &&\n nonempty(author.displayName)\n );\n}\n\n/**\n * Sends one provider work event to the callback provisioned for this install.\n *\n * @remarks\n * Pass the callbackUrl and secret received through configure unchanged. The\n * event's required dedupeKey identifies the logical provider signal and must be\n * reused on retries. Complete normalized content and an opaque hydrateRef are\n * mutually exclusive. The exact serialized UTF-8 body is signed with HMAC-SHA-256.\n * This callback is independent of declared alert and banner webhooks.\n */\nexport async function sendWorkEvent(\n event: WorkEvent,\n options: SendWorkEventOptions,\n): Promise<void> {\n if (!validEvent(event))\n throw new TypeError(\n 'Invalid work event: provide complete normalized content or a nonempty hydrateRef, with routing and identity fields.',\n );\n if (!nonempty(options.callbackUrl))\n throw new TypeError(\n 'A work event requires the callbackUrl provisioned through configure.',\n );\n\n const body = JSON.stringify(event);\n const signature = await signWebhook(options.secret, body);\n const response = await (options.fetch ?? fetch)(options.callbackUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n let rejection: string | null = null;\n try {\n rejection = readText(readRecord(await response.json(), 'error'), 'code');\n } catch {\n // Proxies may return a non-JSON rejection body.\n }\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for work event ${event.type}. Check the callback URL and per-install work-event signing secret provisioned through configure.`,\n response.status,\n );\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/events.ts","../src/server/work-events.ts"],"sourcesContent":["import { TapServerError } from '../client/errors';\nimport type { WebhookName, WebhookPayload } from '../client/types';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { readRecord, readText } from './wire';\n\n/** The body `sendEvent` posts to Tappify, and the body the signature covers. */\nexport interface WebhookEnvelope {\n /** The declared webhook name. */\n event: string;\n /** The extension id the event came from. */\n source: string;\n /** When the event happened, as an ISO timestamp. */\n occurredAt: string;\n /** The build the event belongs to, when it belongs to one. */\n build?: string;\n /** The key Tappify deduplicates on, so a retry is not counted twice. */\n dedupeKey: string;\n payload: Record<string, unknown>;\n}\n\n/** The third argument of `sendEvent`. */\nexport interface SendEventOptions {\n /** Which install the event belongs to. */\n installId: string;\n extensionId: string;\n /** The inbound signing secret from the extension's Server page. */\n secret: string;\n /** A full url to post to, which replaces `baseUrl` and the built path. */\n endpoint?: string;\n /** The Tappify origin to build the path under. Defaults to production. */\n baseUrl?: string;\n /** Defaults to a fresh UUID, so give your own to make a retry idempotent. */\n dedupeKey?: string;\n build?: string;\n /** Defaults to now, in ISO form. */\n occurredAt?: string;\n /** A `fetch` of your own, for a test or a proxy. Defaults to the global one. */\n fetch?: typeof fetch;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.tappify.ai';\n\nfunction toHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map(byte => byte.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Returns the hex HMAC-SHA-256 of a raw body under a shared secret.\n *\n * @remarks\n * This is the value `sendEvent` sends as `X-Tappify-Signature`, computed over the\n * exact bytes of the body it posts. Call it directly only when you build the\n * request yourself, and sign the serialised string you send rather than\n * re-serialising the object, because a different key order is a different\n * signature. Uses Web Crypto, so it runs on Node, Cloudflare Workers and Vercel\n * alike.\n *\n * @example\n * ```ts\n * import { signWebhook } from '@tappify/extension-sdk/server';\n *\n * async function signed(body: string): Promise<Record<string, string>> {\n * return { 'x-tappify-signature': await signWebhook('whsec_test', body) };\n * }\n * ```\n */\nexport async function signWebhook(\n secret: string,\n body: string,\n): Promise<string> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(body));\n return toHex(signature);\n}\n\nasync function rejectionCode(response: Response): Promise<string | null> {\n try {\n const payload: unknown = await response.json();\n return readText(readRecord(payload, 'error'), 'code');\n } catch {\n return null;\n }\n}\n\n/**\n * Posts one of your declared webhook events to Tappify, signed with the install's\n * inbound secret.\n *\n * @remarks\n * The name and the payload are narrowed by the `TapWebhookMap` augmentation\n * `tappify extension types` writes. It builds the envelope, signs the serialised\n * body and posts it to the install's hook path under `baseUrl`, or to `endpoint`\n * when you give one. A response Tappify did not accept rejects with a\n * `TapServerError` whose `code` is always `TAP_WEBHOOK_REJECTED`, carrying the HTTP\n * status, and with Tappify's own code in the message text rather than in `code`;\n * the two usual causes are the wrong signing\n * secret and an event the manifest does not declare. Pass your own `dedupeKey` to\n * make a retry idempotent, because the default is a fresh UUID.\n *\n * @example\n * ```ts\n * import { sendEvent } from '@tappify/extension-sdk/server';\n *\n * async function reportAnomaly(installId: string, why: string): Promise<void> {\n * await sendEvent('anomaly.detected', { why }, {\n * installId,\n * extensionId: 'starter',\n * secret: process.env.TAPPIFY_INBOUND_SECRET ?? '',\n * dedupeKey: `anomaly-${installId}-${why}`,\n * });\n * }\n * ```\n */\nexport async function sendEvent<E extends WebhookName>(\n name: E,\n payload: WebhookPayload<E>,\n options: SendEventOptions,\n): Promise<void> {\n const envelope: WebhookEnvelope = {\n event: name,\n source: options.extensionId,\n occurredAt: options.occurredAt ?? new Date().toISOString(),\n dedupeKey: options.dedupeKey ?? crypto.randomUUID(),\n payload: { ...payload },\n };\n\n if (options.build !== undefined) envelope.build = options.build;\n\n const body = JSON.stringify(envelope);\n const signature = await signWebhook(options.secret, body);\n const url =\n options.endpoint ??\n `${options.baseUrl ?? DEFAULT_BASE_URL}/api/v1/extensions/hooks/${options.extensionId}/${options.installId}`;\n\n const send = options.fetch ?? fetch;\n const response = await send(url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n const rejection = await rejectionCode(response);\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for ${name}. Check the inbound signing secret on the extension's Server page, and that the event is declared under contributes.webhooks.`,\n response.status,\n );\n }\n}\n","import { TapServerError } from '../client/errors';\nimport { SERVER_ERROR_CODES } from './codes';\nimport { signWebhook } from './events';\nimport { readRecord, readText, readUnknown } from './wire';\nimport type { WorkHydratedEvent, WorkHydratedEventBase } from './work';\n\n/** An opaque provider signal to hydrate using current install credentials. */\nexport interface WorkHydrationReferenceEvent extends WorkHydratedEventBase {\n type: WorkHydratedEvent['type'];\n /** A nonempty, short-lived reference resolved by the hydrate_event handler. */\n hydrateRef: string;\n /** Normalized comment content must not accompany a reference. */\n comment?: never;\n /** Normalized status content must not accompany a reference. */\n status?: never;\n}\n\n/** Complete normalized content or an opaque reference, with stable retry identity. */\nexport type WorkEvent =\n | (WorkHydratedEvent & { hydrateRef?: never })\n | WorkHydrationReferenceEvent;\n\n/** Install-specific delivery values provisioned through work configure. */\nexport interface SendWorkEventOptions {\n /** The exact provisioned callback URL; no production default is supplied. */\n callbackUrl: string;\n /** The per-install work-event signing secret provisioned with the callback. */\n secret: string;\n /** A transport for tests or a proxy; defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nfunction nonempty(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction validEvent(event: WorkEvent): boolean {\n const type = readText(event, 'type');\n if (\n !['comment.created', 'status.changed', 'item.deleted'].includes(type ?? '')\n )\n return false;\n if (\n !['externalItemId', 'providerEventId', 'occurredAt', 'dedupeKey'].every(\n key => nonempty(readUnknown(event, key)),\n )\n )\n return false;\n\n const reference = readUnknown(event, 'hydrateRef');\n const comment = readUnknown(event, 'comment');\n const status = readUnknown(event, 'status');\n if (reference !== undefined)\n return nonempty(reference) && comment === undefined && status === undefined;\n\n if (type === 'item.deleted')\n return comment === undefined && status === undefined;\n if (type === 'status.changed') {\n const normalized = readRecord(event, 'status');\n return (\n comment === undefined &&\n nonempty(normalized.id) &&\n nonempty(normalized.label) &&\n typeof normalized.stage === 'string' &&\n ['open', 'in_progress', 'done'].includes(normalized.stage)\n );\n }\n\n const normalized = readRecord(event, 'comment');\n const author = readRecord(normalized, 'author');\n return (\n status === undefined &&\n nonempty(normalized.externalCommentId) &&\n typeof normalized.body === 'string' &&\n nonempty(normalized.createdAt) &&\n nonempty(author.id) &&\n nonempty(author.displayName)\n );\n}\n\n/**\n * Sends one provider work event to the callback provisioned for this install.\n *\n * @remarks\n * Pass the callbackUrl and secret received through configure unchanged. The\n * event's required dedupeKey identifies the logical provider signal and must be\n * reused on retries. Complete normalized content and an opaque hydrateRef are\n * mutually exclusive. The exact serialized UTF-8 body is signed with HMAC-SHA-256.\n * This callback is independent of declared alert and banner webhooks.\n */\nexport async function sendWorkEvent(\n event: WorkEvent,\n options: SendWorkEventOptions,\n): Promise<void> {\n if (!validEvent(event))\n throw new TypeError(\n 'Invalid work event: provide complete normalized content or a nonempty hydrateRef, with routing and identity fields.',\n );\n if (!nonempty(options.callbackUrl))\n throw new TypeError(\n 'A work event requires the callbackUrl provisioned through configure.',\n );\n\n const body = JSON.stringify(event);\n const signature = await signWebhook(options.secret, body);\n const response = await (options.fetch ?? fetch)(options.callbackUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-signature': signature,\n },\n body,\n });\n\n if (!response.ok) {\n let rejection: string | null = null;\n try {\n rejection = readText(readRecord(await response.json(), 'error'), 'code');\n } catch {\n // Proxies may return a non-JSON rejection body.\n }\n throw new TapServerError(\n SERVER_ERROR_CODES.WEBHOOK_REJECTED,\n `Tappify answered ${String(response.status)}${rejection === null ? '' : ` (${rejection})`} for work event ${event.type}. Check the callback URL and per-install work-event signing secret provisioned through configure.`,\n response.status,\n );\n }\n}\n"],"mappings":"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","names":["normalized"]}
|