@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/testing/index.cjs
CHANGED
|
@@ -1,775 +1,42 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var _chunk5AAPD5WVcjs = require('../chunk-5AAPD5WV.cjs');
|
|
5
4
|
|
|
5
|
+
var _chunkO357OJXLcjs = require('../chunk-O357OJXL.cjs');
|
|
6
6
|
|
|
7
|
-
var _chunk7KIKOH5Hcjs = require('../chunk-7KIKOH5H.cjs');
|
|
8
|
-
require('../chunk-OPPYEA7F.cjs');
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
|
|
14
|
-
var _chunkRMUWRESKcjs = require('../chunk-RMUWRESK.cjs');
|
|
15
12
|
|
|
16
13
|
|
|
17
14
|
|
|
18
15
|
|
|
19
|
-
var _chunkPYJMJF44cjs = require('../chunk-PYJMJF44.cjs');
|
|
20
16
|
|
|
21
17
|
|
|
22
18
|
|
|
23
19
|
|
|
24
|
-
var _chunk3YWVYA42cjs = require('../chunk-3YWVYA42.cjs');
|
|
25
20
|
|
|
26
21
|
|
|
22
|
+
var _chunkBPCGXXMDcjs = require('../chunk-BPCGXXMD.cjs');
|
|
27
23
|
|
|
28
|
-
var _chunk2DBSVKAUcjs = require('../chunk-2DBSVKAU.cjs');
|
|
29
24
|
|
|
30
|
-
// src/testing/client.ts
|
|
31
25
|
|
|
26
|
+
var _chunkGAUZYBXEcjs = require('../chunk-GAUZYBXE.cjs');
|
|
32
27
|
|
|
33
28
|
|
|
29
|
+
var _chunkVV7ELNRWcjs = require('../chunk-VV7ELNRW.cjs');
|
|
30
|
+
require('../chunk-PRCZMOLD.cjs');
|
|
34
31
|
|
|
35
|
-
var _jose = require('jose');
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
range: { from: "2026-09-01", to: "2026-09-08", preset: "7d" },
|
|
40
|
-
platform: "all",
|
|
41
|
-
country: "all"
|
|
42
|
-
};
|
|
43
|
-
function callEnvelope(envelope) {
|
|
44
|
-
const context = {
|
|
45
|
-
projectId: envelope.install.projectId,
|
|
46
|
-
platform: envelope.filters.platform,
|
|
47
|
-
from: envelope.filters.range.from,
|
|
48
|
-
to: envelope.filters.range.to,
|
|
49
|
-
filters: envelope.filters
|
|
50
|
-
};
|
|
51
|
-
return JSON.stringify({
|
|
52
|
-
install: envelope.install,
|
|
53
|
-
context,
|
|
54
|
-
input: envelope.input,
|
|
55
|
-
credentials: _nullishCoalesce(envelope.credentials, () => ( {})),
|
|
56
|
-
documents: _nullishCoalesce(envelope.documents, () => ( {})),
|
|
57
|
-
event: envelope.event
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
function parseJson(text) {
|
|
61
|
-
try {
|
|
62
|
-
return { parsed: true, value: JSON.parse(text) };
|
|
63
|
-
} catch (e) {
|
|
64
|
-
return { parsed: false, value: void 0 };
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
async function readEnvelopeResponse(response, call) {
|
|
68
|
-
const text = await response.text();
|
|
69
|
-
const body = parseJson(text);
|
|
70
|
-
const status = String(response.status);
|
|
71
|
-
if (response.ok) {
|
|
72
|
-
if (body.parsed) return body.value;
|
|
73
|
-
if (text.trim() === "") return void 0;
|
|
74
|
-
throw new (0, _chunk2DBSVKAUcjs.TapServerError)(
|
|
75
|
-
_chunkRMUWRESKcjs.SERVER_ERROR_CODES.INTERNAL_ERROR,
|
|
76
|
-
`The extension server answered ${status} for ${call} with a body that is not JSON. Every Tappify route answers JSON, or 204 with no body.`,
|
|
77
|
-
response.status
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
const error = _chunkRMUWRESKcjs.readRecord.call(void 0, body.value, "error");
|
|
81
|
-
throw new (0, _chunk2DBSVKAUcjs.TapServerError)(
|
|
82
|
-
_nullishCoalesce(_chunkRMUWRESKcjs.readText.call(void 0, error, "code"), () => ( _chunkRMUWRESKcjs.SERVER_ERROR_CODES.INTERNAL_ERROR)),
|
|
83
|
-
_nullishCoalesce(_chunkRMUWRESKcjs.readText.call(void 0, error, "message"), () => ( `The extension server answered ${status} for ${call} without a JSON error body.`)),
|
|
84
|
-
response.status
|
|
85
|
-
);
|
|
86
|
-
}
|
|
33
|
+
var _chunkEB5XWQAUcjs = require('../chunk-EB5XWQAU.cjs');
|
|
34
|
+
require('../chunk-A74HCO6T.cjs');
|
|
87
35
|
|
|
88
|
-
// src/testing/client.ts
|
|
89
|
-
var TEST_KID = "tappify-test-key";
|
|
90
|
-
var TEST_ISSUER = "https://api.tappify.ai";
|
|
91
|
-
var keys = null;
|
|
92
|
-
function ensureKeys() {
|
|
93
|
-
_nullishCoalesce(keys, () => ( (keys = (async () => {
|
|
94
|
-
const pair = await _jose.generateKeyPair.call(void 0, "RS256", { extractable: true });
|
|
95
|
-
const publicJwk = await _jose.exportJWK.call(void 0, pair.publicKey);
|
|
96
|
-
return {
|
|
97
|
-
privateKey: pair.privateKey,
|
|
98
|
-
jwks: {
|
|
99
|
-
keys: [{ ...publicJwk, kid: TEST_KID, alg: "RS256", use: "sig" }]
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
})())));
|
|
103
|
-
return keys;
|
|
104
|
-
}
|
|
105
|
-
async function testJwks() {
|
|
106
|
-
return (await ensureKeys()).jwks;
|
|
107
|
-
}
|
|
108
|
-
async function signTestToken(claims = {}, options = {}) {
|
|
109
|
-
const { privateKey } = await ensureKeys();
|
|
110
|
-
const extensionId = _nullishCoalesce(_nullishCoalesce(options.extensionId, () => ( claims.extensionId)), () => ( "starter"));
|
|
111
|
-
return new (0, _jose.SignJWT)({
|
|
112
|
-
installId: _nullishCoalesce(claims.installId, () => ( "ins_test")),
|
|
113
|
-
extensionId,
|
|
114
|
-
projectId: _nullishCoalesce(claims.projectId, () => ( "prj_north")),
|
|
115
|
-
organizationId: _nullishCoalesce(claims.organizationId, () => ( "org_test")),
|
|
116
|
-
scopes: _nullishCoalesce(claims.scopes, () => ( [])),
|
|
117
|
-
...claims.userId === void 0 ? {} : { userId: claims.userId }
|
|
118
|
-
}).setProtectedHeader({ alg: "RS256", kid: TEST_KID }).setIssuer(TEST_ISSUER).setAudience(`ext:${extensionId}`).setIssuedAt().setExpirationTime(_nullishCoalesce(options.expiresIn, () => ( "5m"))).sign(privateKey);
|
|
119
|
-
}
|
|
120
|
-
var HEALTH_PATH = "/tappify/health";
|
|
121
|
-
function createTestClient(target, defaults = {}) {
|
|
122
|
-
if (typeof target === "function") {
|
|
123
|
-
throw new (0, _chunk2DBSVKAUcjs.TapError)(
|
|
124
|
-
"TAP_TEST_TARGET_INVALID",
|
|
125
|
-
"createTestClient() was handed a built handler instead of the options object. A handler from createTappifyHandler() verifies install tokens against the live Tappify key set, so every call the test client makes would come back 401. Pass the options themselves \u2014 createTestClient(handlerOptions) \u2014 or, when you need the handler, build it with createTappifyHandler({ ...handlerOptions, jwks: await testJwks() }) and drive it through createTapMock({ handler, extensionId: handlerOptions.extensionId }) so the test token carries the audience the handler expects."
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
const extensionId = target.extensionId;
|
|
129
|
-
const handlerPromise = testJwks().then(
|
|
130
|
-
(jwks) => _chunkRMUWRESKcjs.createTappifyHandler.call(void 0, { ...target, jwks })
|
|
131
|
-
);
|
|
132
|
-
const install = {
|
|
133
|
-
id: "ins_test",
|
|
134
|
-
extensionId,
|
|
135
|
-
projectId: "prj_north",
|
|
136
|
-
organizationId: "org_test",
|
|
137
|
-
...defaults.install
|
|
138
|
-
};
|
|
139
|
-
const filters = _nullishCoalesce(defaults.filters, () => ( DEFAULT_FILTERS));
|
|
140
|
-
const origin = _nullishCoalesce(defaults.origin, () => ( "https://vendor.test"));
|
|
141
|
-
let sent = 0;
|
|
142
|
-
function envelope(input, event) {
|
|
143
|
-
return callEnvelope({
|
|
144
|
-
install,
|
|
145
|
-
filters,
|
|
146
|
-
input,
|
|
147
|
-
credentials: defaults.credentials,
|
|
148
|
-
documents: defaults.documents,
|
|
149
|
-
event
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
async function send(method, path, body) {
|
|
153
|
-
const handler = await handlerPromise;
|
|
154
|
-
sent += 1;
|
|
155
|
-
const headers = new Headers({
|
|
156
|
-
"content-type": "application/json",
|
|
157
|
-
"x-tappify-event-id": _nullishCoalesce(defaults.eventId, () => ( `evt_${path}_${String(sent)}`))
|
|
158
|
-
});
|
|
159
|
-
if (path !== HEALTH_PATH) {
|
|
160
|
-
const token = await signTestToken(
|
|
161
|
-
{
|
|
162
|
-
installId: install.id,
|
|
163
|
-
extensionId: install.extensionId,
|
|
164
|
-
projectId: install.projectId,
|
|
165
|
-
organizationId: install.organizationId,
|
|
166
|
-
scopes: _nullishCoalesce(defaults.scopes, () => ( []))
|
|
167
|
-
},
|
|
168
|
-
{ extensionId }
|
|
169
|
-
);
|
|
170
|
-
headers.set("authorization", `Bearer ${token}`);
|
|
171
|
-
}
|
|
172
|
-
return handler(
|
|
173
|
-
new Request(`${origin}${path}`, {
|
|
174
|
-
method,
|
|
175
|
-
headers,
|
|
176
|
-
...body === void 0 ? {} : { body }
|
|
177
|
-
})
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
async function call(method, path, body) {
|
|
181
|
-
return readEnvelopeResponse(
|
|
182
|
-
await send(method, path, body),
|
|
183
|
-
`${method} ${path}`
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
return {
|
|
187
|
-
health: async () => await call("GET", HEALTH_PATH),
|
|
188
|
-
metrics: async (input) => await call(
|
|
189
|
-
"POST",
|
|
190
|
-
"/tappify/metrics",
|
|
191
|
-
envelope(input)
|
|
192
|
-
),
|
|
193
|
-
tool: (id, input) => call("POST", `/tappify/tools/${id}`, envelope(input)),
|
|
194
|
-
mention: async (id, query) => await call(
|
|
195
|
-
"GET",
|
|
196
|
-
`/tappify/mentions/${id}?q=${encodeURIComponent(query)}`
|
|
197
|
-
),
|
|
198
|
-
action: (id, input) => call("POST", `/tappify/actions/${id}`, envelope(input)),
|
|
199
|
-
procedure: (name, input = {}) => call("POST", `/tappify/procedures/${name}`, envelope(input)),
|
|
200
|
-
context: async (id) => await call("GET", `/tappify/context/${id}`),
|
|
201
|
-
event: async (name, payload) => {
|
|
202
|
-
await call("POST", "/tappify/events", envelope({}, { name, payload }));
|
|
203
|
-
},
|
|
204
|
-
work: (operation, input) => call("POST", `/tappify/work/${operation}`, envelope(input)),
|
|
205
|
-
raw: (method, path, body) => send(method, path, body === void 0 ? void 0 : JSON.stringify(body))
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
36
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
id: "app_ios",
|
|
213
|
-
name: "Northlight",
|
|
214
|
-
platform: "ios",
|
|
215
|
-
storeId: "1490000001",
|
|
216
|
-
bundleId: "ai.tappify.northlight"
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
id: "app_android",
|
|
220
|
-
name: "Northlight",
|
|
221
|
-
platform: "android",
|
|
222
|
-
storeId: "ai.tappify.northlight",
|
|
223
|
-
bundleId: "ai.tappify.northlight"
|
|
224
|
-
}
|
|
225
|
-
];
|
|
226
|
-
var fixtureReleases = [
|
|
227
|
-
{
|
|
228
|
-
id: "rel_42",
|
|
229
|
-
version: "4.2.0",
|
|
230
|
-
build: "118",
|
|
231
|
-
platform: "ios",
|
|
232
|
-
state: "ready_for_sale",
|
|
233
|
-
shippedAt: "2026-09-01T09:00:00.000Z"
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
id: "rel_41",
|
|
237
|
-
version: "4.1.0",
|
|
238
|
-
build: "107",
|
|
239
|
-
platform: "ios",
|
|
240
|
-
state: "ready_for_sale",
|
|
241
|
-
shippedAt: "2026-08-11T09:00:00.000Z"
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
id: "rel_a42",
|
|
245
|
-
version: "4.2.0",
|
|
246
|
-
build: "4200",
|
|
247
|
-
platform: "android",
|
|
248
|
-
state: "published",
|
|
249
|
-
shippedAt: "2026-09-02T09:00:00.000Z"
|
|
250
|
-
}
|
|
251
|
-
];
|
|
252
|
-
var fixtureKeywords = [
|
|
253
|
-
{
|
|
254
|
-
id: "kw_1",
|
|
255
|
-
term: "photo editor",
|
|
256
|
-
platform: "ios",
|
|
257
|
-
country: "US",
|
|
258
|
-
position: 4,
|
|
259
|
-
popularity: 6.6
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
id: "kw_2",
|
|
263
|
-
term: "collage maker",
|
|
264
|
-
platform: "ios",
|
|
265
|
-
country: "US",
|
|
266
|
-
position: 11,
|
|
267
|
-
popularity: 5.3
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
id: "kw_3",
|
|
271
|
-
term: "photo editor",
|
|
272
|
-
platform: "android",
|
|
273
|
-
country: "US",
|
|
274
|
-
position: 7,
|
|
275
|
-
popularity: 6
|
|
276
|
-
}
|
|
277
|
-
];
|
|
278
|
-
var fixtureListings = [
|
|
279
|
-
{
|
|
280
|
-
platform: "ios",
|
|
281
|
-
country: "US",
|
|
282
|
-
locale: "en-US",
|
|
283
|
-
title: "Northlight \u2014 Photo Editor",
|
|
284
|
-
subtitle: "Edit, collage, share",
|
|
285
|
-
description: "Northlight turns a camera roll into a finished post.",
|
|
286
|
-
keywords: ["photo", "editor", "collage"],
|
|
287
|
-
updatedAt: "2026-08-28T12:00:00.000Z"
|
|
288
|
-
}
|
|
289
|
-
];
|
|
290
|
-
var fixtureProject = {
|
|
291
|
-
project: {
|
|
292
|
-
id: "prj_north",
|
|
293
|
-
name: "Northlight",
|
|
294
|
-
platforms: ["ios", "android"]
|
|
295
|
-
},
|
|
296
|
-
apps: fixtureApps,
|
|
297
|
-
releases: fixtureReleases
|
|
298
|
-
};
|
|
299
|
-
var SERIES_VALUES = {
|
|
300
|
-
downloads: [1180, 1240, 1310, 1290, 1402, 1512, 1488],
|
|
301
|
-
impressions: [21400, 22150, 22980, 22110, 24020, 25510, 25010],
|
|
302
|
-
page_views: [8120, 8340, 8710, 8290, 9110, 9640, 9420],
|
|
303
|
-
conversion_rate: [0.145, 0.149, 0.15, 0.156, 0.152, 0.157, 0.158]
|
|
304
|
-
};
|
|
305
|
-
function week() {
|
|
306
|
-
return [
|
|
307
|
-
"2026-09-01",
|
|
308
|
-
"2026-09-02",
|
|
309
|
-
"2026-09-03",
|
|
310
|
-
"2026-09-04",
|
|
311
|
-
"2026-09-05",
|
|
312
|
-
"2026-09-06",
|
|
313
|
-
"2026-09-07"
|
|
314
|
-
];
|
|
315
|
-
}
|
|
316
|
-
function fixtureSeries(metric) {
|
|
317
|
-
const points = week().map((at, index) => ({
|
|
318
|
-
at,
|
|
319
|
-
value: SERIES_VALUES[metric][index]
|
|
320
|
-
}));
|
|
321
|
-
return {
|
|
322
|
-
metric,
|
|
323
|
-
unit: metric === "conversion_rate" ? "ratio" : "count",
|
|
324
|
-
points
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
var fixtureReviews = {
|
|
328
|
-
total: 3,
|
|
329
|
-
averageRating: 3.7,
|
|
330
|
-
reviews: [
|
|
331
|
-
{
|
|
332
|
-
id: "rev_1",
|
|
333
|
-
platform: "ios",
|
|
334
|
-
country: "US",
|
|
335
|
-
rating: 5,
|
|
336
|
-
title: "Fast and clean",
|
|
337
|
-
body: "The new collage layouts are exactly what I wanted.",
|
|
338
|
-
author: "kbrady",
|
|
339
|
-
submittedAt: "2026-09-05T18:22:00.000Z",
|
|
340
|
-
replied: false
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
id: "rev_2",
|
|
344
|
-
platform: "ios",
|
|
345
|
-
country: "GB",
|
|
346
|
-
rating: 2,
|
|
347
|
-
title: "Crashes on export",
|
|
348
|
-
body: "Since 4.2 the export button closes the app.",
|
|
349
|
-
author: null,
|
|
350
|
-
submittedAt: "2026-09-04T08:10:00.000Z",
|
|
351
|
-
replied: true
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
id: "rev_3",
|
|
355
|
-
platform: "android",
|
|
356
|
-
country: "DE",
|
|
357
|
-
rating: 4,
|
|
358
|
-
title: null,
|
|
359
|
-
body: "Gute App, aber der Export dauert lange.",
|
|
360
|
-
author: "mgruber",
|
|
361
|
-
submittedAt: "2026-09-03T16:45:00.000Z",
|
|
362
|
-
replied: false
|
|
363
|
-
}
|
|
364
|
-
]
|
|
365
|
-
};
|
|
366
|
-
var fixtureCrashes = {
|
|
367
|
-
crashFreeRate: 99.42,
|
|
368
|
-
issues: [
|
|
369
|
-
{
|
|
370
|
-
id: "crash_1",
|
|
371
|
-
title: "ExportSession.finish \u2014 index out of range",
|
|
372
|
-
count: 214,
|
|
373
|
-
build: "118",
|
|
374
|
-
firstSeenAt: "2026-09-01T11:02:00.000Z",
|
|
375
|
-
lastSeenAt: "2026-09-07T21:40:00.000Z"
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
id: "crash_2",
|
|
379
|
-
title: "PhotoLibrary.load \u2014 permission revoked",
|
|
380
|
-
count: 37,
|
|
381
|
-
build: "118",
|
|
382
|
-
firstSeenAt: "2026-09-02T07:15:00.000Z",
|
|
383
|
-
lastSeenAt: "2026-09-06T13:05:00.000Z"
|
|
384
|
-
}
|
|
385
|
-
]
|
|
386
|
-
};
|
|
387
|
-
var fixtureRevenue = {
|
|
388
|
-
currency: "USD",
|
|
389
|
-
mrr: 41200,
|
|
390
|
-
refunds: 18,
|
|
391
|
-
points: week().map((at, index) => ({
|
|
392
|
-
at,
|
|
393
|
-
value: [1310, 1355, 1401, 1388, 1470, 1525, 1502][index]
|
|
394
|
-
}))
|
|
395
|
-
};
|
|
396
|
-
var fixtures = {
|
|
397
|
-
project: fixtureProject,
|
|
398
|
-
apps: fixtureApps,
|
|
399
|
-
releases: fixtureReleases,
|
|
400
|
-
keywords: fixtureKeywords,
|
|
401
|
-
listings: fixtureListings,
|
|
402
|
-
reviews: fixtureReviews,
|
|
403
|
-
crashes: fixtureCrashes,
|
|
404
|
-
revenue: fixtureRevenue,
|
|
405
|
-
series: fixtureSeries
|
|
406
|
-
};
|
|
407
|
-
function answerFixtureQuery(query) {
|
|
408
|
-
switch (query.kind) {
|
|
409
|
-
case "project":
|
|
410
|
-
return structuredClone(fixtureProject);
|
|
411
|
-
case "series":
|
|
412
|
-
return structuredClone(fixtureSeries(query.metric));
|
|
413
|
-
case "keywords":
|
|
414
|
-
return structuredClone({ keywords: fixtureKeywords });
|
|
415
|
-
case "listing":
|
|
416
|
-
return structuredClone({ listings: fixtureListings });
|
|
417
|
-
case "reviews":
|
|
418
|
-
return structuredClone(fixtureReviews);
|
|
419
|
-
case "crashes":
|
|
420
|
-
return structuredClone(fixtureCrashes);
|
|
421
|
-
case "revenue":
|
|
422
|
-
return structuredClone(fixtureRevenue);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// src/testing/mock.ts
|
|
427
|
-
var SINGLETON_ID = "singleton";
|
|
428
|
-
var PROBE_KEYS = /* @__PURE__ */ new Set([
|
|
429
|
-
"then",
|
|
430
|
-
"toJSON",
|
|
431
|
-
"$$typeof",
|
|
432
|
-
"constructor",
|
|
433
|
-
"prototype",
|
|
434
|
-
"valueOf",
|
|
435
|
-
"toString",
|
|
436
|
-
"inspect",
|
|
437
|
-
"asymmetricMatch",
|
|
438
|
-
"nodeType"
|
|
439
|
-
]);
|
|
440
|
-
function unknownCollectionError(collection) {
|
|
441
|
-
return new (0, _chunk2DBSVKAUcjs.TapError)(
|
|
442
|
-
"TAP_UNKNOWN_COLLECTION",
|
|
443
|
-
`This mock exposes no storage collection called "${collection}". createTapMock({ storage: \u2026 }) was given an explicit map, so only the collections it names exist. Add "${collection}" to that map, or drop the storage option to expose every collection.`
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
function singletonView(handle) {
|
|
447
|
-
return {
|
|
448
|
-
get: () => handle.get(),
|
|
449
|
-
set: (document2) => handle.set(document2),
|
|
450
|
-
patch: (partial) => handle.patch(partial)
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
function collectionView(handle) {
|
|
454
|
-
return {
|
|
455
|
-
list: (listOptions) => handle.list(listOptions),
|
|
456
|
-
get: (id) => handle.get(id),
|
|
457
|
-
put: (id, document2) => handle.put(id, document2),
|
|
458
|
-
delete: (id) => handle.delete(id)
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
function scopeError(scope) {
|
|
462
|
-
return new (0, _chunk2DBSVKAUcjs.TapError)(
|
|
463
|
-
"TAP_SCOPE_MISSING",
|
|
464
|
-
`This install has not granted ${scope}. Declare it under "scopes" in tappify.extension.json, or pass scopes: ["${scope}"] to renderWithTap() to test the granted path.`
|
|
465
|
-
);
|
|
466
|
-
}
|
|
467
|
-
function asObject(value) {
|
|
468
|
-
return typeof value === "object" && value !== null ? value : {};
|
|
469
|
-
}
|
|
470
|
-
function createTapMock(options = {}) {
|
|
471
|
-
const granted = new Set(_nullishCoalesce(options.scopes, () => ( _chunkPYJMJF44cjs.SCOPE_KEYS)));
|
|
472
|
-
const listeners = /* @__PURE__ */ new Map();
|
|
473
|
-
const eventHandlers = /* @__PURE__ */ new Map();
|
|
474
|
-
const state = /* @__PURE__ */ new Map();
|
|
475
|
-
const store = /* @__PURE__ */ new Map();
|
|
476
|
-
const portal = document.createElement("div");
|
|
477
|
-
portal.dataset.tapPortal = "true";
|
|
478
|
-
const calls = {
|
|
479
|
-
navigations: [],
|
|
480
|
-
searches: [],
|
|
481
|
-
toasts: [],
|
|
482
|
-
confirms: [],
|
|
483
|
-
actions: [],
|
|
484
|
-
telemetry: [],
|
|
485
|
-
chats: [],
|
|
486
|
-
externals: [],
|
|
487
|
-
downloads: [],
|
|
488
|
-
copies: [],
|
|
489
|
-
cards: [],
|
|
490
|
-
expands: 0,
|
|
491
|
-
settingsOpens: 0
|
|
492
|
-
};
|
|
493
|
-
const emitChange = (key) => {
|
|
494
|
-
for (const listener of _nullishCoalesce(listeners.get(key), () => ( []))) listener();
|
|
495
|
-
};
|
|
496
|
-
const subscribe = (key, listener) => {
|
|
497
|
-
const set = _nullishCoalesce(listeners.get(key), () => ( /* @__PURE__ */ new Set()));
|
|
498
|
-
set.add(listener);
|
|
499
|
-
listeners.set(key, set);
|
|
500
|
-
return () => {
|
|
501
|
-
set.delete(listener);
|
|
502
|
-
};
|
|
503
|
-
};
|
|
504
|
-
const withStorage = (run) => granted.has("storage:write") ? Promise.resolve(run()) : Promise.reject(scopeError("storage:write"));
|
|
505
|
-
const bucket = (collection) => {
|
|
506
|
-
const existing = store.get(collection);
|
|
507
|
-
if (existing) return existing;
|
|
508
|
-
const created = [];
|
|
509
|
-
store.set(collection, created);
|
|
510
|
-
return created;
|
|
511
|
-
};
|
|
512
|
-
function buildHandle(collection) {
|
|
513
|
-
const find = (id) => bucket(collection).find((entry) => entry.id === id);
|
|
514
|
-
const write = (id, data) => {
|
|
515
|
-
const items = bucket(collection);
|
|
516
|
-
const index = items.findIndex((entry) => entry.id === id);
|
|
517
|
-
if (index === -1) items.push({ id, data });
|
|
518
|
-
else items[index] = { id, data };
|
|
519
|
-
emitChange(`storage:${collection}`);
|
|
520
|
-
};
|
|
521
|
-
return {
|
|
522
|
-
get: (id) => withStorage(() => _nullishCoalesce(_optionalChain([find, 'call', _ => _(_nullishCoalesce(id, () => ( SINGLETON_ID))), 'optionalAccess', _2 => _2.data]), () => ( null))),
|
|
523
|
-
set: (document2) => withStorage(() => {
|
|
524
|
-
write(SINGLETON_ID, document2);
|
|
525
|
-
}),
|
|
526
|
-
patch: (partial) => withStorage(() => {
|
|
527
|
-
const merged = { ...asObject(_optionalChain([find, 'call', _3 => _3(SINGLETON_ID), 'optionalAccess', _4 => _4.data])), ...partial };
|
|
528
|
-
write(SINGLETON_ID, merged);
|
|
529
|
-
return merged;
|
|
530
|
-
}),
|
|
531
|
-
list: () => withStorage(() => ({
|
|
532
|
-
items: bucket(collection).filter((entry) => entry.id !== SINGLETON_ID).map((entry) => ({ ...asObject(entry.data), id: entry.id }))
|
|
533
|
-
})),
|
|
534
|
-
put: (id, document2) => withStorage(() => {
|
|
535
|
-
write(id, document2);
|
|
536
|
-
}),
|
|
537
|
-
delete: (id) => withStorage(() => {
|
|
538
|
-
const items = bucket(collection);
|
|
539
|
-
const index = items.findIndex((entry) => entry.id === id);
|
|
540
|
-
if (index !== -1) {
|
|
541
|
-
items.splice(index, 1);
|
|
542
|
-
emitChange(`storage:${collection}`);
|
|
543
|
-
}
|
|
544
|
-
})
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
const handles = /* @__PURE__ */ new Map();
|
|
548
|
-
const kinds = options.storage;
|
|
549
|
-
const exposeHandle = (collection) => {
|
|
550
|
-
const kind = _optionalChain([kinds, 'optionalAccess', _5 => _5[collection]]);
|
|
551
|
-
if (kinds !== void 0 && kind === void 0) {
|
|
552
|
-
throw unknownCollectionError(collection);
|
|
553
|
-
}
|
|
554
|
-
const handle = buildHandle(collection);
|
|
555
|
-
if (kind === "singleton") return singletonView(handle);
|
|
556
|
-
if (kind === "collection") return collectionView(handle);
|
|
557
|
-
return handle;
|
|
558
|
-
};
|
|
559
|
-
const handleFor = (collection) => {
|
|
560
|
-
if (PROBE_KEYS.has(collection)) return void 0;
|
|
561
|
-
const existing = handles.get(collection);
|
|
562
|
-
if (existing) return existing;
|
|
563
|
-
const created = exposeHandle(collection);
|
|
564
|
-
handles.set(collection, created);
|
|
565
|
-
return created;
|
|
566
|
-
};
|
|
567
|
-
const storageTarget = {};
|
|
568
|
-
const storage = new Proxy(storageTarget, {
|
|
569
|
-
get: (_target, property) => typeof property === "string" ? handleFor(property) : void 0,
|
|
570
|
-
has: () => true,
|
|
571
|
-
ownKeys: () => [...store.keys()],
|
|
572
|
-
getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true })
|
|
573
|
-
});
|
|
574
|
-
let calledProcedures = 0;
|
|
575
|
-
let signedToken = null;
|
|
576
|
-
function handlerToken() {
|
|
577
|
-
if (options.handlerToken !== void 0) {
|
|
578
|
-
return Promise.resolve(options.handlerToken);
|
|
579
|
-
}
|
|
580
|
-
_nullishCoalesce(signedToken, () => ( (signedToken = signTestToken(
|
|
581
|
-
{
|
|
582
|
-
installId: tap.extension.installId,
|
|
583
|
-
extensionId: tap.extension.id,
|
|
584
|
-
projectId: tap.extension.projectId,
|
|
585
|
-
organizationId: tap.auth.organizationId,
|
|
586
|
-
scopes: [...granted]
|
|
587
|
-
},
|
|
588
|
-
{ extensionId: tap.extension.id }
|
|
589
|
-
))));
|
|
590
|
-
return signedToken;
|
|
591
|
-
}
|
|
592
|
-
async function callProcedure(name, input) {
|
|
593
|
-
const local = _optionalChain([options, 'access', _6 => _6.server, 'optionalAccess', _7 => _7[name]]);
|
|
594
|
-
if (local) return local(input);
|
|
595
|
-
if (options.handler) {
|
|
596
|
-
const path = `/tappify/procedures/${name}`;
|
|
597
|
-
calledProcedures += 1;
|
|
598
|
-
const response = await options.handler(
|
|
599
|
-
new Request(`https://vendor.test${path}`, {
|
|
600
|
-
method: "POST",
|
|
601
|
-
headers: {
|
|
602
|
-
"content-type": "application/json",
|
|
603
|
-
"x-tappify-event-id": `evt_${name}_${String(calledProcedures)}`,
|
|
604
|
-
authorization: `Bearer ${await handlerToken()}`
|
|
605
|
-
},
|
|
606
|
-
body: callEnvelope({
|
|
607
|
-
install: {
|
|
608
|
-
id: tap.extension.installId,
|
|
609
|
-
extensionId: tap.extension.id,
|
|
610
|
-
projectId: tap.extension.projectId,
|
|
611
|
-
organizationId: tap.auth.organizationId
|
|
612
|
-
},
|
|
613
|
-
filters: tap.filters,
|
|
614
|
-
input
|
|
615
|
-
})
|
|
616
|
-
})
|
|
617
|
-
);
|
|
618
|
-
return readEnvelopeResponse(response, `POST ${path}`);
|
|
619
|
-
}
|
|
620
|
-
throw new (0, _chunk2DBSVKAUcjs.TapError)(
|
|
621
|
-
"TAP_UNKNOWN_PROCEDURE",
|
|
622
|
-
`No handler is registered for the procedure "${name}". Pass server: { ${name}: input => \u2026 } or handler: <your createTappifyHandler handler> to createTapMock().`
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
const serverTarget = {};
|
|
626
|
-
const tap = {
|
|
627
|
-
extension: {
|
|
628
|
-
id: _nullishCoalesce(options.extensionId, () => ( "starter")),
|
|
629
|
-
name: _nullishCoalesce(options.extensionName, () => ( "Starter")),
|
|
630
|
-
installId: _nullishCoalesce(options.installId, () => ( "ins_test")),
|
|
631
|
-
projectId: "prj_north"
|
|
632
|
-
},
|
|
633
|
-
env: "dev",
|
|
634
|
-
host: { version: "test", sdkMajors: [2] },
|
|
635
|
-
auth: {
|
|
636
|
-
token: "test-token",
|
|
637
|
-
user: { id: "usr_test", role: "admin" },
|
|
638
|
-
organizationId: "org_test",
|
|
639
|
-
scopes: [...granted],
|
|
640
|
-
can: (scope) => granted.has(scope)
|
|
641
|
-
},
|
|
642
|
-
project: {
|
|
643
|
-
id: "prj_north",
|
|
644
|
-
name: "Northlight",
|
|
645
|
-
platforms: ["ios", "android"],
|
|
646
|
-
apps: [],
|
|
647
|
-
releases: [],
|
|
648
|
-
keywords: [],
|
|
649
|
-
...options.project
|
|
650
|
-
},
|
|
651
|
-
filters: { ...DEFAULT_FILTERS, ...options.filters },
|
|
652
|
-
theme: { mode: _nullishCoalesce(options.theme, () => ( "light")) },
|
|
653
|
-
locale: "en-US",
|
|
654
|
-
timezone: "UTC",
|
|
655
|
-
format: {
|
|
656
|
-
number: (value, formatOptions) => new Intl.NumberFormat("en-US", formatOptions).format(value),
|
|
657
|
-
currency: (value, code) => new Intl.NumberFormat("en-US", {
|
|
658
|
-
style: "currency",
|
|
659
|
-
currency: code
|
|
660
|
-
}).format(value),
|
|
661
|
-
date: (value) => new Date(value).toISOString().slice(0, 10),
|
|
662
|
-
relative: (value) => new Date(value).toISOString()
|
|
663
|
-
},
|
|
664
|
-
nav: {
|
|
665
|
-
push: (path) => calls.navigations.push(path),
|
|
666
|
-
setSearch: (params) => calls.searches.push(params),
|
|
667
|
-
openSettings: () => {
|
|
668
|
-
calls.settingsOpens += 1;
|
|
669
|
-
},
|
|
670
|
-
openChat: (prompt, context) => calls.chats.push({ prompt, context }),
|
|
671
|
-
openExternal: (url) => calls.externals.push(url)
|
|
672
|
-
},
|
|
673
|
-
data: {
|
|
674
|
-
query: (query) => {
|
|
675
|
-
const scope = _chunkPYJMJF44cjs.QUERY_SCOPES[query.kind];
|
|
676
|
-
if (!granted.has(scope)) return Promise.reject(scopeError(scope));
|
|
677
|
-
return Promise.resolve(answerFixtureQuery(query));
|
|
678
|
-
},
|
|
679
|
-
subscribe: (event, handler) => {
|
|
680
|
-
const key = String(event);
|
|
681
|
-
const set = _nullishCoalesce(eventHandlers.get(key), () => ( /* @__PURE__ */ new Set()));
|
|
682
|
-
const wrapped = (payload) => {
|
|
683
|
-
handler(payload);
|
|
684
|
-
};
|
|
685
|
-
set.add(wrapped);
|
|
686
|
-
eventHandlers.set(key, set);
|
|
687
|
-
return () => {
|
|
688
|
-
set.delete(wrapped);
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
},
|
|
692
|
-
server: new Proxy(serverTarget, {
|
|
693
|
-
get: (_target, property) => (input) => callProcedure(String(property), input),
|
|
694
|
-
has: () => true
|
|
695
|
-
}),
|
|
696
|
-
state: {
|
|
697
|
-
get: (key) => {
|
|
698
|
-
const value = state.get(key);
|
|
699
|
-
return value === void 0 ? void 0 : value;
|
|
700
|
-
},
|
|
701
|
-
set: (key, value) => {
|
|
702
|
-
state.set(key, value);
|
|
703
|
-
emitChange(`state:${key}`);
|
|
704
|
-
},
|
|
705
|
-
subscribe: (key, handler) => subscribe(`state:${key}`, handler)
|
|
706
|
-
},
|
|
707
|
-
ui: {
|
|
708
|
-
size: _nullishCoalesce(options.size, () => ( "slot")),
|
|
709
|
-
toast: (message, tone = "neutral") => calls.toasts.push({ message, tone }),
|
|
710
|
-
confirm: (confirmOptions) => {
|
|
711
|
-
calls.confirms.push(confirmOptions);
|
|
712
|
-
return Promise.resolve(_nullishCoalesce(options.confirmAnswer, () => ( true)));
|
|
713
|
-
},
|
|
714
|
-
openInChat: (card) => calls.cards.push(card),
|
|
715
|
-
expand: () => {
|
|
716
|
-
calls.expands += 1;
|
|
717
|
-
},
|
|
718
|
-
download: (blob, filename) => calls.downloads.push({ filename, size: blob.size }),
|
|
719
|
-
copy: (text) => {
|
|
720
|
-
calls.copies.push(text);
|
|
721
|
-
return Promise.resolve();
|
|
722
|
-
}
|
|
723
|
-
},
|
|
724
|
-
context: _nullishCoalesce(options.context, () => ( {})),
|
|
725
|
-
params: _nullishCoalesce(options.params, () => ( {})),
|
|
726
|
-
storage,
|
|
727
|
-
telemetry: {
|
|
728
|
-
event: (name, props) => calls.telemetry.push({ name, props })
|
|
729
|
-
},
|
|
730
|
-
actions: {
|
|
731
|
-
run: (actionId, input) => {
|
|
732
|
-
calls.actions.push({ actionId, input });
|
|
733
|
-
return Promise.resolve({
|
|
734
|
-
id: `run_${String(calls.actions.length)}`,
|
|
735
|
-
actionId,
|
|
736
|
-
status: "pending",
|
|
737
|
-
requestedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
738
|
-
});
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
invalidate: () => void 0,
|
|
742
|
-
__portal: portal,
|
|
743
|
-
__subscribe: subscribe
|
|
744
|
-
};
|
|
745
|
-
return {
|
|
746
|
-
tap,
|
|
747
|
-
calls,
|
|
748
|
-
portal,
|
|
749
|
-
documents: (collection, documentId) => bucket(collection).filter((entry) => documentId === void 0 || entry.id === documentId).map((entry) => entry.data),
|
|
750
|
-
emit: (event, payload) => {
|
|
751
|
-
for (const handler of _nullishCoalesce(eventHandlers.get(String(event)), () => ( []))) {
|
|
752
|
-
handler(payload);
|
|
753
|
-
}
|
|
754
|
-
},
|
|
755
|
-
setFilters: (filters) => {
|
|
756
|
-
tap.filters = filters;
|
|
757
|
-
emitChange("filters");
|
|
758
|
-
},
|
|
759
|
-
setSize: (size) => {
|
|
760
|
-
tap.ui = { ...tap.ui, size };
|
|
761
|
-
emitChange("size");
|
|
762
|
-
},
|
|
763
|
-
setTheme: (mode) => {
|
|
764
|
-
tap.theme = { mode };
|
|
765
|
-
emitChange("theme");
|
|
766
|
-
},
|
|
767
|
-
setParams: (params) => {
|
|
768
|
-
tap.params = params;
|
|
769
|
-
emitChange("params");
|
|
770
|
-
}
|
|
771
|
-
};
|
|
772
|
-
}
|
|
37
|
+
var _chunk7YKH4LZNcjs = require('../chunk-7YKH4LZN.cjs');
|
|
38
|
+
require('../chunk-6HETNZJY.cjs');
|
|
39
|
+
require('../chunk-Q7SFCCGT.cjs');
|
|
773
40
|
|
|
774
41
|
// src/testing/render.tsx
|
|
775
42
|
var _reactquery = require('@tanstack/react-query');
|
|
@@ -778,18 +45,18 @@ var _reactquery = require('@tanstack/react-query');
|
|
|
778
45
|
var _react = require('@testing-library/react');
|
|
779
46
|
var _jsxruntime = require('react/jsx-runtime');
|
|
780
47
|
function renderWithTap(ui, options = {}) {
|
|
781
|
-
const mock = _nullishCoalesce(options.mock, () => ( createTapMock(options)));
|
|
48
|
+
const mock = _nullishCoalesce(options.mock, () => ( _chunkBPCGXXMDcjs.createTapMock.call(void 0, options)));
|
|
782
49
|
const queryClient = _nullishCoalesce(options.queryClient, () => ( new (0, _reactquery.QueryClient)({
|
|
783
50
|
defaultOptions: { queries: { retry: false, gcTime: 0 } }
|
|
784
51
|
})));
|
|
785
52
|
const installId = mock.tap.extension.installId;
|
|
786
53
|
mock.tap.invalidate = (name) => {
|
|
787
54
|
void queryClient.invalidateQueries({
|
|
788
|
-
queryKey: name === void 0 ?
|
|
55
|
+
queryKey: name === void 0 ? _chunkGAUZYBXEcjs.installPrefix.call(void 0, installId) : _chunkGAUZYBXEcjs.procedurePrefix.call(void 0, installId, name)
|
|
789
56
|
});
|
|
790
57
|
};
|
|
791
58
|
function Wrapper({ children }) {
|
|
792
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactquery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
59
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactquery.QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVV7ELNRWcjs.TapHostProvider, { tap: mock.tap, children }) });
|
|
793
60
|
}
|
|
794
61
|
const result = _react.render.call(void 0, ui, { wrapper: Wrapper, container: options.container });
|
|
795
62
|
result.baseElement.append(mock.portal);
|
|
@@ -808,7 +75,7 @@ function isPoint(value) {
|
|
|
808
75
|
return pair !== null && pair.length === 2 && typeof pair[0] === "string" && typeof pair[1] === "number";
|
|
809
76
|
}
|
|
810
77
|
function validateMetricsResponse(response, metrics) {
|
|
811
|
-
const series = list(_optionalChain([record, 'call',
|
|
78
|
+
const series = list(_optionalChain([record, 'call', _ => _(response), 'optionalAccess', _2 => _2.series]));
|
|
812
79
|
if (series === null) {
|
|
813
80
|
return [
|
|
814
81
|
'A metrics response is { "series": [...] }. Return one entry per declared metric, even when it has no points.'
|
|
@@ -818,7 +85,7 @@ function validateMetricsResponse(response, metrics) {
|
|
|
818
85
|
const declared = new Map(metrics.map((metric) => [metric.key, metric]));
|
|
819
86
|
for (const [index, entry] of series.entries()) {
|
|
820
87
|
const item = record(entry);
|
|
821
|
-
const key = typeof _optionalChain([item, 'optionalAccess',
|
|
88
|
+
const key = typeof _optionalChain([item, 'optionalAccess', _3 => _3.metric]) === "string" ? item.metric : null;
|
|
822
89
|
if (item === null || key === null) {
|
|
823
90
|
issues.push(`series[${String(index)}] has no "metric" key.`);
|
|
824
91
|
continue;
|
|
@@ -857,11 +124,67 @@ function validateToolResponse(value, returns) {
|
|
|
857
124
|
`A "${returns}" tool returns an object the host draws as a card, not ${typeof value}.`
|
|
858
125
|
];
|
|
859
126
|
}
|
|
860
|
-
return
|
|
127
|
+
return _chunk7YKH4LZNcjs.CARD_KEYS[returns].filter((key) => !(key in body)).map(
|
|
861
128
|
(key) => `A "${returns}" tool response needs a "${key}" key; the host draws the card from it.`
|
|
862
129
|
);
|
|
863
130
|
}
|
|
864
131
|
|
|
132
|
+
// src/testing/work-events.ts
|
|
133
|
+
function workCommentCreatedFixture(overrides = {}) {
|
|
134
|
+
return {
|
|
135
|
+
type: "comment.created",
|
|
136
|
+
externalItemId: "item_test",
|
|
137
|
+
providerEventId: "event_comment_test",
|
|
138
|
+
occurredAt: "2026-10-15T12:00:00.000Z",
|
|
139
|
+
dedupeKey: "item_test:event_comment_test",
|
|
140
|
+
comment: {
|
|
141
|
+
externalCommentId: "comment_test",
|
|
142
|
+
body: "A provider comment.",
|
|
143
|
+
author: { id: "author_test", displayName: "Sam" },
|
|
144
|
+
createdAt: "2026-10-15T12:00:00.000Z"
|
|
145
|
+
},
|
|
146
|
+
...overrides
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function workStatusChangedFixture(overrides = {}) {
|
|
150
|
+
return {
|
|
151
|
+
type: "status.changed",
|
|
152
|
+
externalItemId: "item_test",
|
|
153
|
+
providerEventId: "event_status_test",
|
|
154
|
+
occurredAt: "2026-10-15T12:00:00.000Z",
|
|
155
|
+
dedupeKey: "item_test:event_status_test",
|
|
156
|
+
status: { id: "resolved_test", label: "Resolved", stage: "done" },
|
|
157
|
+
...overrides
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function workItemDeletedFixture(overrides = {}) {
|
|
161
|
+
return {
|
|
162
|
+
type: "item.deleted",
|
|
163
|
+
externalItemId: "item_test",
|
|
164
|
+
providerEventId: "event_deleted_test",
|
|
165
|
+
occurredAt: "2026-10-15T12:00:00.000Z",
|
|
166
|
+
dedupeKey: "item_test:event_deleted_test",
|
|
167
|
+
...overrides
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function signedWorkEventRequest(event, options) {
|
|
171
|
+
let request;
|
|
172
|
+
await _chunkEB5XWQAUcjs.sendWorkEvent.call(void 0, event, {
|
|
173
|
+
...options,
|
|
174
|
+
fetch: (input, init) => {
|
|
175
|
+
request = new Request(input, init);
|
|
176
|
+
return Promise.resolve(new Response(null, { status: 202 }));
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
if (!request)
|
|
180
|
+
throw new Error("The work event sender did not produce a request.");
|
|
181
|
+
return request;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
865
188
|
|
|
866
189
|
|
|
867
190
|
|
|
@@ -883,5 +206,5 @@ function validateToolResponse(value, returns) {
|
|
|
883
206
|
|
|
884
207
|
|
|
885
208
|
|
|
886
|
-
exports.answerFixtureQuery = answerFixtureQuery; exports.createTapMock = createTapMock; exports.createTestClient = createTestClient; exports.fixtureApps = fixtureApps; exports.fixtureCrashes = fixtureCrashes; exports.fixtureKeywords = fixtureKeywords; exports.fixtureListings = fixtureListings; exports.fixtureProject = fixtureProject; exports.fixtureReleases = fixtureReleases; exports.fixtureRevenue = fixtureRevenue; exports.fixtureReviews = fixtureReviews; exports.fixtureSeries = fixtureSeries; exports.fixtures = fixtures; exports.installHostTheme =
|
|
209
|
+
exports.answerFixtureQuery = _chunkBPCGXXMDcjs.answerFixtureQuery; exports.createTapMock = _chunkBPCGXXMDcjs.createTapMock; exports.createTestClient = _chunkBPCGXXMDcjs.createTestClient; exports.fixtureApps = _chunkBPCGXXMDcjs.fixtureApps; exports.fixtureCrashes = _chunkBPCGXXMDcjs.fixtureCrashes; exports.fixtureKeywords = _chunkBPCGXXMDcjs.fixtureKeywords; exports.fixtureListings = _chunkBPCGXXMDcjs.fixtureListings; exports.fixtureProject = _chunkBPCGXXMDcjs.fixtureProject; exports.fixtureReleases = _chunkBPCGXXMDcjs.fixtureReleases; exports.fixtureRevenue = _chunkBPCGXXMDcjs.fixtureRevenue; exports.fixtureReviews = _chunkBPCGXXMDcjs.fixtureReviews; exports.fixtureSeries = _chunkBPCGXXMDcjs.fixtureSeries; exports.fixtures = _chunkBPCGXXMDcjs.fixtures; exports.installHostTheme = _chunkO357OJXLcjs.installHostTheme; exports.registerTapMatchers = _chunkO357OJXLcjs.registerTapMatchers; exports.renderWithTap = renderWithTap; exports.signTestToken = _chunkBPCGXXMDcjs.signTestToken; exports.signedWorkEventRequest = signedWorkEventRequest; exports.tapMatchers = _chunkO357OJXLcjs.tapMatchers; exports.testJwks = _chunkBPCGXXMDcjs.testJwks; exports.validateMetricsResponse = validateMetricsResponse; exports.validateToolResponse = validateToolResponse; exports.workCommentCreatedFixture = workCommentCreatedFixture; exports.workItemDeletedFixture = workItemDeletedFixture; exports.workStatusChangedFixture = workStatusChangedFixture;
|
|
887
210
|
//# sourceMappingURL=index.cjs.map
|