@tappify/extension-sdk 2.0.0 → 2.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +208 -0
- package/dist/chunk-2CY5HVY7.mjs +59 -0
- package/dist/chunk-2CY5HVY7.mjs.map +1 -0
- package/dist/{chunk-2DBSVKAU.cjs → chunk-6HETNZJY.cjs} +1 -1
- package/dist/chunk-6HETNZJY.cjs.map +1 -0
- package/dist/{chunk-PYJMJF44.cjs → chunk-7YKH4LZN.cjs} +9 -2
- package/dist/chunk-7YKH4LZN.cjs.map +1 -0
- package/dist/{chunk-RMUWRESK.cjs → chunk-A74HCO6T.cjs} +31 -18
- package/dist/chunk-A74HCO6T.cjs.map +1 -0
- package/dist/chunk-BPCGXXMD.cjs +781 -0
- package/dist/chunk-BPCGXXMD.cjs.map +1 -0
- package/dist/chunk-EB5XWQAU.cjs +132 -0
- package/dist/chunk-EB5XWQAU.cjs.map +1 -0
- package/dist/{chunk-5AAPD5WV.cjs → chunk-GAUZYBXE.cjs} +11 -11
- package/dist/chunk-GAUZYBXE.cjs.map +1 -0
- package/dist/chunk-J5LGTIGS.mjs +10 -0
- package/dist/chunk-J5LGTIGS.mjs.map +1 -0
- package/dist/{chunk-ZT35X7KL.mjs → chunk-KZ5NGFUP.mjs} +22 -9
- package/dist/chunk-KZ5NGFUP.mjs.map +1 -0
- package/dist/chunk-NN263PGQ.cjs +70 -0
- package/dist/chunk-NN263PGQ.cjs.map +1 -0
- package/dist/{chunk-3YWVYA42.cjs → chunk-O357OJXL.cjs} +14 -18
- package/dist/chunk-O357OJXL.cjs.map +1 -0
- package/dist/chunk-ONJMPO3P.cjs +59 -0
- package/dist/chunk-ONJMPO3P.cjs.map +1 -0
- package/dist/{chunk-OPPYEA7F.cjs → chunk-PRCZMOLD.cjs} +3 -3
- package/dist/chunk-PRCZMOLD.cjs.map +1 -0
- package/dist/chunk-Q7SFCCGT.cjs +10 -0
- package/dist/chunk-Q7SFCCGT.cjs.map +1 -0
- package/dist/{chunk-QHM7XNRK.mjs → chunk-QA5AL3VZ.mjs} +2 -2
- package/dist/{chunk-QHM7XNRK.mjs.map → chunk-QA5AL3VZ.mjs.map} +1 -1
- package/dist/chunk-QJMZRV7A.mjs +132 -0
- package/dist/chunk-QJMZRV7A.mjs.map +1 -0
- package/dist/{chunk-CSTYBKZC.mjs → chunk-QXD7WVKK.mjs} +1 -1
- package/dist/chunk-QXD7WVKK.mjs.map +1 -0
- package/dist/{chunk-4C5Z3CIK.mjs → chunk-RBIQT75L.mjs} +2 -2
- package/dist/chunk-RBIQT75L.mjs.map +1 -0
- package/dist/{chunk-MNDVYPEP.mjs → chunk-SGCYEEXD.mjs} +9 -2
- package/dist/chunk-SGCYEEXD.mjs.map +1 -0
- package/dist/{chunk-BDCX6LRQ.mjs → chunk-SGEYDV3K.mjs} +3 -3
- package/dist/chunk-SGEYDV3K.mjs.map +1 -0
- package/dist/{chunk-IGK2AP6J.mjs → chunk-SLGOHIBL.mjs} +13 -17
- package/dist/chunk-SLGOHIBL.mjs.map +1 -0
- package/dist/chunk-SWG7CNTZ.mjs +781 -0
- package/dist/chunk-SWG7CNTZ.mjs.map +1 -0
- package/dist/{chunk-7KIKOH5H.cjs → chunk-VV7ELNRW.cjs} +3 -3
- package/dist/chunk-VV7ELNRW.cjs.map +1 -0
- package/dist/chunk-Z43DPNZ6.mjs +70 -0
- package/dist/chunk-Z43DPNZ6.mjs.map +1 -0
- package/dist/client-BO3kavn0.d.ts +25 -0
- package/dist/client-DE-vMgdO.d.cts +25 -0
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +43 -7
- package/dist/codegen.d.ts +43 -7
- package/dist/codegen.mjs.map +1 -1
- package/dist/{constants-DJ_KgZpN.d.cts → constants-Bh759FUI.d.cts} +44 -8
- package/dist/{constants-DJ_KgZpN.d.ts → constants-Bh759FUI.d.ts} +44 -8
- package/dist/{context-Dt6xh_5t.d.cts → context-DI0EJCRD.d.cts} +5 -4
- package/dist/{context-Ml7k_V14.d.ts → context-mr8R5CNt.d.ts} +5 -4
- package/dist/{errors-COsBh7kd.d.cts → errors-Cqh0wrqt.d.cts} +3 -2
- package/dist/{errors-COsBh7kd.d.ts → errors-Cqh0wrqt.d.ts} +3 -2
- package/dist/host.cjs +5 -4
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts +5 -5
- package/dist/host.d.ts +5 -5
- package/dist/host.mjs +4 -3
- package/dist/host.mjs.map +1 -1
- package/dist/index.cjs +9 -656
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -627
- package/dist/index.d.ts +69 -627
- package/dist/index.mjs +5 -652
- package/dist/index.mjs.map +1 -1
- package/dist/manifest.cjs +142 -86
- package/dist/manifest.cjs.map +1 -1
- package/dist/manifest.d.cts +97 -12
- package/dist/manifest.d.ts +97 -12
- package/dist/manifest.mjs +129 -73
- package/dist/manifest.mjs.map +1 -1
- package/dist/server.cjs +15 -66
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +6 -6
- package/dist/server.d.ts +6 -6
- package/dist/server.mjs +10 -61
- package/dist/server.mjs.map +1 -1
- package/dist/testing/index.cjs +73 -750
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +30 -11
- package/dist/testing/index.d.ts +30 -11
- package/dist/testing/index.mjs +89 -766
- package/dist/testing/index.mjs.map +1 -1
- package/dist/testing/jest.cjs +6 -5
- package/dist/testing/jest.cjs.map +1 -1
- package/dist/testing/jest.mjs +3 -2
- package/dist/testing/jest.mjs.map +1 -1
- package/dist/testing/vitest.cjs +6 -5
- package/dist/testing/vitest.cjs.map +1 -1
- package/dist/testing/vitest.d.cts +10 -0
- package/dist/testing/vitest.d.ts +10 -0
- package/dist/testing/vitest.mjs +3 -2
- package/dist/testing/vitest.mjs.map +1 -1
- package/dist/{types-DDAGLpov.d.ts → types-BdDaLnqs.d.cts} +4 -3
- package/dist/{types-DrpQSv_2.d.cts → types-uGHrRaOj.d.ts} +4 -3
- package/dist/ui/styles.css +740 -0
- package/dist/ui.cjs +1018 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.d.cts +231 -0
- package/dist/ui.d.ts +231 -0
- package/dist/ui.mjs +1018 -0
- package/dist/ui.mjs.map +1 -0
- package/dist/vite-preview-bootstrap.cjs +66 -0
- package/dist/vite-preview-bootstrap.cjs.map +1 -0
- package/dist/vite-preview-bootstrap.d.cts +15 -0
- package/dist/vite-preview-bootstrap.d.ts +15 -0
- package/dist/vite-preview-bootstrap.mjs +66 -0
- package/dist/vite-preview-bootstrap.mjs.map +1 -0
- package/dist/vite-preview.cjs +876 -0
- package/dist/vite-preview.cjs.map +1 -0
- package/dist/vite-preview.d.cts +9 -0
- package/dist/vite-preview.d.ts +9 -0
- package/dist/vite-preview.mjs +876 -0
- package/dist/vite-preview.mjs.map +1 -0
- package/dist/vite.cjs +320 -60
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +54 -8
- package/dist/vite.d.ts +54 -8
- package/dist/vite.mjs +312 -54
- package/dist/vite.mjs.map +1 -1
- package/dist/work-events-BlOVpK0k.d.ts +547 -0
- package/dist/work-events-D97EoqvS.d.cts +547 -0
- package/package.json +19 -2
- package/schema/extension-v2.json +440 -121
- package/dist/chunk-2DBSVKAU.cjs.map +0 -1
- package/dist/chunk-3YWVYA42.cjs.map +0 -1
- package/dist/chunk-4C5Z3CIK.mjs.map +0 -1
- package/dist/chunk-5AAPD5WV.cjs.map +0 -1
- package/dist/chunk-7KIKOH5H.cjs.map +0 -1
- package/dist/chunk-BDCX6LRQ.mjs.map +0 -1
- package/dist/chunk-CSTYBKZC.mjs.map +0 -1
- package/dist/chunk-IGK2AP6J.mjs.map +0 -1
- package/dist/chunk-MNDVYPEP.mjs.map +0 -1
- package/dist/chunk-OPPYEA7F.cjs.map +0 -1
- package/dist/chunk-PYJMJF44.cjs.map +0 -1
- package/dist/chunk-RMUWRESK.cjs.map +0 -1
- package/dist/chunk-ZT35X7KL.mjs.map +0 -1
- package/dist/handler-CZIlZ5vu.d.ts +0 -227
- package/dist/handler-kU9Jf36K.d.cts +0 -227
- package/dist/styles.css +0 -367
package/dist/vite.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/vite/plugin.ts
|
|
2
2
|
import { federation } from "@module-federation/vite";
|
|
3
3
|
import { readFileSync as readFileSync2 } from "fs";
|
|
4
|
-
import { resolve as
|
|
4
|
+
import { resolve as resolve3, sep as sep2 } from "path";
|
|
5
5
|
|
|
6
6
|
// src/client/errors.ts
|
|
7
7
|
var TAP_ERROR_CODES = [
|
|
@@ -143,6 +143,13 @@ var SCOPES = [
|
|
|
143
143
|
requiresSecurityReview: false,
|
|
144
144
|
requiresJustification: false
|
|
145
145
|
},
|
|
146
|
+
{
|
|
147
|
+
key: "review.threads:read",
|
|
148
|
+
label: "Read Review threads",
|
|
149
|
+
description: "Review threads, comments, screenshot descriptors, build context and safe device context",
|
|
150
|
+
requiresSecurityReview: false,
|
|
151
|
+
requiresJustification: false
|
|
152
|
+
},
|
|
146
153
|
{
|
|
147
154
|
key: "crashes:read",
|
|
148
155
|
label: "Read crash data",
|
|
@@ -216,7 +223,7 @@ var SCOPES = [
|
|
|
216
223
|
{
|
|
217
224
|
key: "work:sync",
|
|
218
225
|
label: "Sync work items",
|
|
219
|
-
description: "
|
|
226
|
+
description: "Sync comments and status on created work items",
|
|
220
227
|
requiresSecurityReview: false,
|
|
221
228
|
requiresJustification: true
|
|
222
229
|
},
|
|
@@ -520,7 +527,10 @@ var connectorContribution = z.object({
|
|
|
520
527
|
),
|
|
521
528
|
scopes: z.array(z.string()).describe(
|
|
522
529
|
"The scopes asked of the outside service. These are that service's scopes, not Tappify's."
|
|
523
|
-
)
|
|
530
|
+
),
|
|
531
|
+
tokenRequestEncoding: z.enum(["form", "json"]).describe(
|
|
532
|
+
"The request body encoding for authorization-code and refresh token exchanges. When omitted, uses form encoding (application/x-www-form-urlencoded); json uses application/json."
|
|
533
|
+
).optional()
|
|
524
534
|
}).strict().describe('The endpoints, for `method: "oauth"`.').optional()
|
|
525
535
|
}).strict().describe("How owners authenticate this connector.").optional(),
|
|
526
536
|
sync: z.enum(["5m", "15m", "1h", "6h", "24h"]).describe("How often Tappify asks your server to sync.").optional(),
|
|
@@ -664,7 +674,7 @@ var aiContribution = z.object({
|
|
|
664
674
|
).optional()
|
|
665
675
|
}).strict();
|
|
666
676
|
var statusNames = (stage) => z.array(z.string()).describe(`Your own status names that mean ${stage}.`);
|
|
667
|
-
var
|
|
677
|
+
var legacyWorkContribution = z.object({
|
|
668
678
|
kind: z.enum(["tracker", "docs", "chat"]).describe("What kind of destination this is."),
|
|
669
679
|
container: z.object({
|
|
670
680
|
label: z.string().min(1).max(60).describe(
|
|
@@ -699,6 +709,55 @@ var workContribution = z.object({
|
|
|
699
709
|
"Whether items are created as the owner who asked (`user`) or as the install itself."
|
|
700
710
|
)
|
|
701
711
|
}).strict();
|
|
712
|
+
var workDirection = z.enum([
|
|
713
|
+
"none",
|
|
714
|
+
"to_provider",
|
|
715
|
+
"from_provider",
|
|
716
|
+
"two-way"
|
|
717
|
+
]);
|
|
718
|
+
var workAuth = z.discriminatedUnion("method", [
|
|
719
|
+
z.object({
|
|
720
|
+
method: z.literal("oauth"),
|
|
721
|
+
provider: z.string().min(1).describe("The OAuth provider identifier."),
|
|
722
|
+
scopes: z.array(z.string()).describe("The scopes requested from the provider.")
|
|
723
|
+
}).strict(),
|
|
724
|
+
z.object({
|
|
725
|
+
method: z.literal("api_key"),
|
|
726
|
+
fields: z.array(credentialField).max(20)
|
|
727
|
+
}).strict()
|
|
728
|
+
]);
|
|
729
|
+
var currentWorkContribution = legacyWorkContribution.extend({
|
|
730
|
+
container: legacyWorkContribution.shape.container.describe(
|
|
731
|
+
"Deprecated. Resolve containers dynamically with describe_container."
|
|
732
|
+
).optional(),
|
|
733
|
+
fields: legacyWorkContribution.shape.fields.describe("Deprecated. Resolve fields dynamically with describe_container.").optional(),
|
|
734
|
+
comments: z.union([
|
|
735
|
+
z.enum(["two-way", "none"]).describe("Deprecated. Declare a comments capability object."),
|
|
736
|
+
z.object({
|
|
737
|
+
direction: workDirection,
|
|
738
|
+
supportsEdits: z.literal(false),
|
|
739
|
+
supportsDeletes: z.literal(false)
|
|
740
|
+
}).strict()
|
|
741
|
+
]),
|
|
742
|
+
status: z.object({
|
|
743
|
+
direction: workDirection,
|
|
744
|
+
webhook: z.string().regex(DOMAIN_EVENT).optional(),
|
|
745
|
+
map: z.object({
|
|
746
|
+
open: statusNames("not started"),
|
|
747
|
+
in_progress: statusNames("under way"),
|
|
748
|
+
done: statusNames("finished")
|
|
749
|
+
}).strict().describe(
|
|
750
|
+
"Deprecated. Resolve statuses dynamically with describe_container."
|
|
751
|
+
).optional()
|
|
752
|
+
}).strict().optional(),
|
|
753
|
+
auth: workAuth.describe("How owners authenticate the work destination.")
|
|
754
|
+
});
|
|
755
|
+
var workContribution = z.union([
|
|
756
|
+
currentWorkContribution,
|
|
757
|
+
legacyWorkContribution.describe(
|
|
758
|
+
"Deprecated SDK v2 work declaration. Migrate to work capabilities and auth."
|
|
759
|
+
)
|
|
760
|
+
]);
|
|
702
761
|
var listing = z.object({
|
|
703
762
|
longDescription: z.string().min(1).max(5e3).describe("The body of your listing page, in markdown.").optional(),
|
|
704
763
|
screenshots: z.array(z.string().min(1)).min(1, "A listing shows 1 to 6 screenshots.").max(6, "A listing shows 1 to 6 screenshots.").describe(
|
|
@@ -790,7 +849,7 @@ var manifestObject = z.object({
|
|
|
790
849
|
"What this extension offers the assistant: tools, mentions, actions, knowledge, skills, prompts and context providers."
|
|
791
850
|
).optional(),
|
|
792
851
|
work: workContribution.describe(
|
|
793
|
-
"The work destination this extension creates issues, pages or messages in. Needs
|
|
852
|
+
"The work destination this extension creates issues, pages or messages in. Needs `review.threads:read` and `work:create`; active comment or status sync also needs `work:sync`."
|
|
794
853
|
).optional()
|
|
795
854
|
}).strict().describe(
|
|
796
855
|
"What this extension adds to the host. At most 20 UI contributions in total, and a page, tab, widget, row action, marker or settings panel needs the `ui:render` scope."
|
|
@@ -1011,16 +1070,36 @@ var manifestSchema = manifestObject.superRefine((manifest, ctx) => {
|
|
|
1011
1070
|
}
|
|
1012
1071
|
}
|
|
1013
1072
|
if (contributes.work) {
|
|
1073
|
+
const work = contributes.work;
|
|
1074
|
+
const legacy = isLegacyWork(work);
|
|
1075
|
+
if (!legacy || !granted.has("reviews:read")) {
|
|
1076
|
+
requireScope(
|
|
1077
|
+
"review.threads:read",
|
|
1078
|
+
["scopes"],
|
|
1079
|
+
"the work destination reads Review threads."
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1014
1082
|
requireScope(
|
|
1015
1083
|
"work:create",
|
|
1016
1084
|
["scopes"],
|
|
1017
1085
|
"the work destination creates items."
|
|
1018
1086
|
);
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1087
|
+
const comments = typeof work.comments === "string" ? work.comments : work.comments.direction;
|
|
1088
|
+
const status = work.status ? "direction" in work.status ? work.status.direction : "from_provider" : "none";
|
|
1089
|
+
if (comments !== "none" || status !== "none") {
|
|
1090
|
+
requireScope(
|
|
1091
|
+
"work:sync",
|
|
1092
|
+
["scopes"],
|
|
1093
|
+
"the work destination syncs comments or status."
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
if (work.identity === "user") {
|
|
1097
|
+
ctx.addIssue({
|
|
1098
|
+
code: "custom",
|
|
1099
|
+
path: ["contributes", "work", "identity"],
|
|
1100
|
+
message: 'Per-user work identity is not supported. Set identity to "install".'
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1024
1103
|
}
|
|
1025
1104
|
const needsServer = contributes.connector !== void 0 || (contributes.webhooks?.length ?? 0) > 0 || ai !== void 0 || contributes.work !== void 0 || manifest.server?.procedures !== void 0;
|
|
1026
1105
|
if (needsServer && !manifest.server?.baseUrl) {
|
|
@@ -1095,10 +1174,176 @@ function validateManifest(value) {
|
|
|
1095
1174
|
}));
|
|
1096
1175
|
return { ok: false, issues };
|
|
1097
1176
|
}
|
|
1177
|
+
function isLegacyWork(work) {
|
|
1178
|
+
return !("auth" in work);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/vite/preview/plugin.ts
|
|
1182
|
+
import { randomUUID } from "crypto";
|
|
1183
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
1184
|
+
import { normalizePath } from "vite";
|
|
1185
|
+
var PREVIEW_CLIENT_ID = "virtual:tappify-preview/client";
|
|
1186
|
+
var PREVIEW_MANIFEST_ID = "virtual:tappify-preview/manifest";
|
|
1187
|
+
var PREVIEW_REMOTE_ID = "virtual:tappify-preview/remote";
|
|
1188
|
+
var PREVIEW_REMOTE_ENV = "TAPPIFY_PREVIEW_REMOTE";
|
|
1189
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
1190
|
+
var VIRTUAL_IDS = /* @__PURE__ */ new Set([
|
|
1191
|
+
PREVIEW_CLIENT_ID,
|
|
1192
|
+
PREVIEW_MANIFEST_ID,
|
|
1193
|
+
PREVIEW_REMOTE_ID
|
|
1194
|
+
]);
|
|
1195
|
+
function previewRemote(remote, port) {
|
|
1196
|
+
if (remote === void 0 || remote.trim() === "") {
|
|
1197
|
+
return { ok: true, url: `http://localhost:${String(port)}/remoteEntry.js` };
|
|
1198
|
+
}
|
|
1199
|
+
let parsed;
|
|
1200
|
+
try {
|
|
1201
|
+
parsed = new URL(remote);
|
|
1202
|
+
} catch {
|
|
1203
|
+
return { ok: false, message: "The preview remote is not a valid URL." };
|
|
1204
|
+
}
|
|
1205
|
+
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && LOOPBACK_HOSTS.has(parsed.hostname))) {
|
|
1206
|
+
return {
|
|
1207
|
+
ok: false,
|
|
1208
|
+
message: "A preview remote must use HTTPS or loopback HTTP."
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
return { ok: true, url: parsed.toString() };
|
|
1212
|
+
}
|
|
1213
|
+
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1214
|
+
function messageDocument(title2, messages) {
|
|
1215
|
+
return `<!doctype html>
|
|
1216
|
+
<html lang="en">
|
|
1217
|
+
<head>
|
|
1218
|
+
<meta charset="UTF-8" />
|
|
1219
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1220
|
+
<title>${escapeHtml(title2)} \xB7 Tappify local preview</title>
|
|
1221
|
+
<style>body{margin:0;min-height:100vh;display:grid;place-content:center;padding:32px;box-sizing:border-box;background:#f4f5ef;color:#142012;font-family:Inter,ui-sans-serif,system-ui,sans-serif}main{max-width:720px;padding:28px;border:1px solid #d8ddcf;border-radius:12px;background:#fff;box-shadow:0 12px 36px rgb(20 32 18 / 9%)}h1{font-size:22px;margin:0 0 14px}ul{margin:0;padding-left:22px;color:#4d5849}li+li{margin-top:8px}</style>
|
|
1222
|
+
</head>
|
|
1223
|
+
<body><main><h1>${escapeHtml(title2)}</h1><ul>${messages.map((message) => `<li>${escapeHtml(message)}</li>`).join("")}</ul></main></body>
|
|
1224
|
+
</html>`;
|
|
1225
|
+
}
|
|
1226
|
+
function previewDocument(manifest, remote) {
|
|
1227
|
+
if (!manifest.ok) {
|
|
1228
|
+
return messageDocument(
|
|
1229
|
+
"Manifest is invalid",
|
|
1230
|
+
manifest.issues.map((issue) => `${issue.path}: ${issue.message}`)
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1233
|
+
if (!remote.ok)
|
|
1234
|
+
return messageDocument("Preview remote was refused", [remote.message]);
|
|
1235
|
+
const icon = manifest.manifest.icon.replace(/^\.\//, "");
|
|
1236
|
+
return `<!doctype html>
|
|
1237
|
+
<html lang="en">
|
|
1238
|
+
<head>
|
|
1239
|
+
<meta charset="UTF-8" />
|
|
1240
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1241
|
+
<link rel="icon" href="/${escapeHtml(icon)}" />
|
|
1242
|
+
<title>${escapeHtml(manifest.manifest.name)} \xB7 Tappify local preview</title>
|
|
1243
|
+
</head>
|
|
1244
|
+
<body>
|
|
1245
|
+
<div id="tappify-preview-root"></div>
|
|
1246
|
+
<script type="module" src="/@id/${PREVIEW_CLIENT_ID}"></script>
|
|
1247
|
+
</body>
|
|
1248
|
+
</html>`;
|
|
1249
|
+
}
|
|
1250
|
+
var moduleSource = (value) => `export default ${JSON.stringify(value)};`;
|
|
1251
|
+
function optimizedReactUrl(root, cacheDir, cacheGeneration) {
|
|
1252
|
+
const depsCacheDir = normalizePath(resolve(cacheDir, "deps"));
|
|
1253
|
+
const relativeCacheDir = normalizePath(relative(root, depsCacheDir));
|
|
1254
|
+
const prefix = isAbsolute(relativeCacheDir) || relativeCacheDir.startsWith("../") ? `/@fs/${depsCacheDir.replace(/^\/+/, "")}` : `/${relativeCacheDir}`;
|
|
1255
|
+
return `${prefix}/react.js?tappify=${encodeURIComponent(cacheGeneration)}`;
|
|
1256
|
+
}
|
|
1257
|
+
function createPreviewPlugins(options) {
|
|
1258
|
+
const remote = previewRemote(options.remote, options.port);
|
|
1259
|
+
const cacheGeneration = randomUUID();
|
|
1260
|
+
let reactUrl = optimizedReactUrl(
|
|
1261
|
+
resolve(options.manifestFile, ".."),
|
|
1262
|
+
resolve(options.manifestFile, "../node_modules/.vite"),
|
|
1263
|
+
cacheGeneration
|
|
1264
|
+
);
|
|
1265
|
+
const preview = {
|
|
1266
|
+
name: "tappify:preview",
|
|
1267
|
+
apply: "serve",
|
|
1268
|
+
config() {
|
|
1269
|
+
return {
|
|
1270
|
+
optimizeDeps: {
|
|
1271
|
+
exclude: ["@tappify/extension-sdk/vite/preview/bootstrap"]
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
},
|
|
1275
|
+
configResolved(config) {
|
|
1276
|
+
reactUrl = optimizedReactUrl(
|
|
1277
|
+
config.root,
|
|
1278
|
+
config.cacheDir,
|
|
1279
|
+
cacheGeneration
|
|
1280
|
+
);
|
|
1281
|
+
},
|
|
1282
|
+
resolveId(id) {
|
|
1283
|
+
if (VIRTUAL_IDS.has(id)) return `\0${id}`;
|
|
1284
|
+
if (id.startsWith("\0") && VIRTUAL_IDS.has(id.slice(1))) return id;
|
|
1285
|
+
return null;
|
|
1286
|
+
},
|
|
1287
|
+
load(id) {
|
|
1288
|
+
if (id === `\0${PREVIEW_MANIFEST_ID}`) {
|
|
1289
|
+
return moduleSource(options.loadManifest());
|
|
1290
|
+
}
|
|
1291
|
+
if (id === `\0${PREVIEW_REMOTE_ID}`) return moduleSource(remote);
|
|
1292
|
+
if (id !== `\0${PREVIEW_CLIENT_ID}`) return null;
|
|
1293
|
+
return [
|
|
1294
|
+
`import { startLocalPreview } from '@tappify/extension-sdk/vite/preview/bootstrap';`,
|
|
1295
|
+
`import manifest from '${PREVIEW_MANIFEST_ID}';`,
|
|
1296
|
+
`import remote from '${PREVIEW_REMOTE_ID}';`,
|
|
1297
|
+
`const mount = document.getElementById('tappify-preview-root');`,
|
|
1298
|
+
`if (mount && manifest.ok && remote.ok) void startLocalPreview({ manifest: manifest.manifest, remote: remote.url, reactUrl: ${JSON.stringify(reactUrl)}, mount });`
|
|
1299
|
+
].join("\n");
|
|
1300
|
+
},
|
|
1301
|
+
configureServer(server) {
|
|
1302
|
+
server.middlewares.use((request, response, next) => {
|
|
1303
|
+
const path = request.url?.split("?", 1)[0];
|
|
1304
|
+
if (path !== "/" && path !== "/index.html") return next();
|
|
1305
|
+
void server.transformIndexHtml(
|
|
1306
|
+
path,
|
|
1307
|
+
previewDocument(options.loadManifest(), remote)
|
|
1308
|
+
).then((html) => {
|
|
1309
|
+
response.statusCode = 200;
|
|
1310
|
+
response.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
1311
|
+
response.end(html);
|
|
1312
|
+
}).catch(next);
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
const dev = {
|
|
1317
|
+
name: "tappify:dev",
|
|
1318
|
+
apply: "serve",
|
|
1319
|
+
config() {
|
|
1320
|
+
return {
|
|
1321
|
+
server: {
|
|
1322
|
+
host: "127.0.0.1",
|
|
1323
|
+
port: options.port,
|
|
1324
|
+
strictPort: true,
|
|
1325
|
+
cors: { origin: true },
|
|
1326
|
+
origin: `http://localhost:${String(options.port)}`
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
},
|
|
1330
|
+
configureServer(server) {
|
|
1331
|
+
server.watcher.add(options.manifestFile);
|
|
1332
|
+
},
|
|
1333
|
+
async hotUpdate(context) {
|
|
1334
|
+
if (this.environment.name !== "client") return;
|
|
1335
|
+
if (context.file !== options.manifestFile) return;
|
|
1336
|
+
const manifest = options.loadManifest();
|
|
1337
|
+
await options.onManifestChange?.(manifest);
|
|
1338
|
+
context.server.ws.send({ type: "full-reload", path: "*" });
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
return [preview, dev];
|
|
1342
|
+
}
|
|
1098
1343
|
|
|
1099
1344
|
// src/vite/styles.ts
|
|
1100
1345
|
import { existsSync, readFileSync } from "fs";
|
|
1101
|
-
import { dirname, extname, resolve, sep } from "path";
|
|
1346
|
+
import { dirname, extname, resolve as resolve2, sep } from "path";
|
|
1102
1347
|
var IMPORT_PATTERN = /(?:^|\n)\s*(?:import|export)(?:[^'"]*?from\s*)?\s*['"]([^'"]+)['"]/g;
|
|
1103
1348
|
var IMPORT_SPECIFIER_PATTERN = /((?:^|\n)\s*(?:import|export)(?:[^'"]*?from\s*)?\s*['"])([^'"]+)(['"])/g;
|
|
1104
1349
|
var BLOCK_COMMENT_PATTERN = /\/\*[\s\S]*?\*\//g;
|
|
@@ -1111,14 +1356,14 @@ function isPlainStylesheet(specifier) {
|
|
|
1111
1356
|
return specifier.endsWith(".css") && !specifier.endsWith(".module.css");
|
|
1112
1357
|
}
|
|
1113
1358
|
function resolveSource(specifier, importer) {
|
|
1114
|
-
const base =
|
|
1359
|
+
const base = resolve2(dirname(importer), specifier);
|
|
1115
1360
|
if (extname(base) !== "" && existsSync(base)) return base;
|
|
1116
1361
|
for (const extension of SOURCE_EXTENSIONS) {
|
|
1117
1362
|
const candidate = `${base}${extension}`;
|
|
1118
1363
|
if (existsSync(candidate)) return candidate;
|
|
1119
1364
|
}
|
|
1120
1365
|
for (const extension of SOURCE_EXTENSIONS) {
|
|
1121
|
-
const candidate =
|
|
1366
|
+
const candidate = resolve2(base, `index${extension}`);
|
|
1122
1367
|
if (existsSync(candidate)) return candidate;
|
|
1123
1368
|
}
|
|
1124
1369
|
return null;
|
|
@@ -1140,7 +1385,7 @@ function collectEntryStyles(entryAbsolutePath, root) {
|
|
|
1140
1385
|
while (match !== null) {
|
|
1141
1386
|
const specifier = match[1];
|
|
1142
1387
|
if (specifier.endsWith(".css")) {
|
|
1143
|
-
const value = specifier.startsWith(".") ?
|
|
1388
|
+
const value = specifier.startsWith(".") ? resolve2(dirname(file), specifier) : specifier;
|
|
1144
1389
|
if (!seenStyles.has(value)) {
|
|
1145
1390
|
seenStyles.add(value);
|
|
1146
1391
|
styles.push(value);
|
|
@@ -1173,38 +1418,52 @@ var DEFAULT_PORT = 5273;
|
|
|
1173
1418
|
var SOURCE_FILE = /\.(?:[cm]?[jt]sx?)$/;
|
|
1174
1419
|
var federationOptionsByPlugins = /* @__PURE__ */ new WeakMap();
|
|
1175
1420
|
function manifestFile(options) {
|
|
1176
|
-
return options.manifestPath ??
|
|
1421
|
+
return options.manifestPath ?? resolve3(options.root ?? process.cwd(), "tappify.extension.json");
|
|
1177
1422
|
}
|
|
1178
1423
|
function loadManifest(options) {
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1424
|
+
let raw;
|
|
1425
|
+
try {
|
|
1426
|
+
raw = options.manifest ?? JSON.parse(readFileSync2(manifestFile(options), "utf8"));
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
return {
|
|
1429
|
+
ok: false,
|
|
1430
|
+
issues: [
|
|
1431
|
+
{
|
|
1432
|
+
path: "$",
|
|
1433
|
+
message: error instanceof Error ? `The manifest is not valid JSON: ${error.message}` : "The manifest is not valid JSON."
|
|
1434
|
+
}
|
|
1435
|
+
]
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
return validateManifest(raw);
|
|
1439
|
+
}
|
|
1440
|
+
function manifestError(result) {
|
|
1441
|
+
if (result.ok) throw new Error("A valid manifest has no validation error.");
|
|
1442
|
+
const lines = result.issues.map((issue) => ` ${issue.path}: ${issue.message}`).join("\n");
|
|
1443
|
+
return new TapError(
|
|
1444
|
+
"TAP_MANIFEST_INVALID",
|
|
1445
|
+
`tappify.extension.json does not validate:
|
|
1186
1446
|
${lines}
|
|
1187
1447
|
Fix the fields above, or run \`tappify extension doctor\` for the same list with suggested fixes.`
|
|
1188
|
-
|
|
1189
|
-
}
|
|
1190
|
-
return result.manifest;
|
|
1448
|
+
);
|
|
1191
1449
|
}
|
|
1192
1450
|
function tappifyExtension(options = {}) {
|
|
1193
1451
|
const root = options.root ?? process.cwd();
|
|
1194
1452
|
const boundary = root.endsWith(sep2) ? root : `${root}${sep2}`;
|
|
1195
|
-
const
|
|
1196
|
-
const
|
|
1453
|
+
const loaded = loadManifest(options);
|
|
1454
|
+
const manifest = loaded.ok ? loaded.manifest : null;
|
|
1455
|
+
const contributions = manifest === null ? [] : uiContributions(manifest);
|
|
1197
1456
|
const port = options.port ?? DEFAULT_PORT;
|
|
1198
1457
|
const entryByKey = /* @__PURE__ */ new Map();
|
|
1199
1458
|
const exposes = {};
|
|
1200
1459
|
for (const contribution of contributions) {
|
|
1201
1460
|
const key = contribution.expose.slice("./".length);
|
|
1202
|
-
entryByKey.set(key,
|
|
1461
|
+
entryByKey.set(key, resolve3(root, contribution.entry));
|
|
1203
1462
|
exposes[contribution.expose] = `${VIRTUAL_PREFIX}${key}`;
|
|
1204
1463
|
}
|
|
1205
1464
|
const singleton = { singleton: true, requiredVersion: false };
|
|
1206
1465
|
const federationOptions = {
|
|
1207
|
-
name: remoteName(manifest
|
|
1466
|
+
name: remoteName(manifest?.id ?? "invalid-extension"),
|
|
1208
1467
|
filename: "remoteEntry.js",
|
|
1209
1468
|
manifest: true,
|
|
1210
1469
|
exposes,
|
|
@@ -1260,6 +1519,16 @@ function tappifyExtension(options = {}) {
|
|
|
1260
1519
|
].join("\n");
|
|
1261
1520
|
}
|
|
1262
1521
|
};
|
|
1522
|
+
const guard = {
|
|
1523
|
+
name: "tappify:manifest",
|
|
1524
|
+
enforce: "pre",
|
|
1525
|
+
config(_config, environment) {
|
|
1526
|
+
if (environment.command !== "build") return null;
|
|
1527
|
+
const current = loadManifest(options);
|
|
1528
|
+
if (!current.ok) throw manifestError(current);
|
|
1529
|
+
return null;
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1263
1532
|
const css = {
|
|
1264
1533
|
name: "tappify:css",
|
|
1265
1534
|
enforce: "pre",
|
|
@@ -1272,43 +1541,32 @@ function tappifyExtension(options = {}) {
|
|
|
1272
1541
|
return next === null ? null : { code: next, map: null };
|
|
1273
1542
|
}
|
|
1274
1543
|
};
|
|
1275
|
-
const dev = {
|
|
1276
|
-
name: "tappify:dev",
|
|
1277
|
-
apply: "serve",
|
|
1278
|
-
config() {
|
|
1279
|
-
return {
|
|
1280
|
-
server: {
|
|
1281
|
-
port,
|
|
1282
|
-
strictPort: true,
|
|
1283
|
-
cors: { origin: true },
|
|
1284
|
-
origin: `http://localhost:${String(port)}`
|
|
1285
|
-
}
|
|
1286
|
-
};
|
|
1287
|
-
},
|
|
1288
|
-
configureServer(server) {
|
|
1289
|
-
server.watcher.add(manifestFile(options));
|
|
1290
|
-
},
|
|
1291
|
-
/** Vite runs hotUpdate once per dev environment, and the manifest is one file. */
|
|
1292
|
-
async hotUpdate(context) {
|
|
1293
|
-
if (this.environment.name !== "client") return;
|
|
1294
|
-
if (context.file !== manifestFile(options)) return;
|
|
1295
|
-
if (options.onManifestChange === void 0) return;
|
|
1296
|
-
await options.onManifestChange(loadManifest(options));
|
|
1297
|
-
}
|
|
1298
|
-
};
|
|
1299
1544
|
const plugins = [
|
|
1545
|
+
guard,
|
|
1300
1546
|
expose,
|
|
1301
1547
|
css,
|
|
1302
1548
|
...federation(federationOptions),
|
|
1303
|
-
|
|
1549
|
+
...createPreviewPlugins({
|
|
1550
|
+
manifestFile: manifestFile(options),
|
|
1551
|
+
loadManifest: () => loadManifest(options),
|
|
1552
|
+
port,
|
|
1553
|
+
remote: process.env[PREVIEW_REMOTE_ENV],
|
|
1554
|
+
onManifestChange: async (result) => {
|
|
1555
|
+
if (result.ok && options.onManifestChange !== void 0) {
|
|
1556
|
+
await options.onManifestChange(result.manifest);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
})
|
|
1304
1560
|
];
|
|
1305
1561
|
federationOptionsByPlugins.set(plugins, federationOptions);
|
|
1306
1562
|
return plugins;
|
|
1307
1563
|
}
|
|
1308
1564
|
export {
|
|
1565
|
+
PREVIEW_REMOTE_ENV,
|
|
1309
1566
|
VIRTUAL_PREFIX,
|
|
1310
1567
|
collectEntryStyles,
|
|
1311
1568
|
inlineCssImports,
|
|
1569
|
+
previewRemote,
|
|
1312
1570
|
tappifyExtension
|
|
1313
1571
|
};
|
|
1314
1572
|
//# sourceMappingURL=vite.mjs.map
|