@tappify/extension-sdk 2.1.0-beta.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +18 -54
  2. package/dist/{chunk-6HETNZJY.cjs → chunk-2DBSVKAU.cjs} +1 -1
  3. package/dist/{chunk-6HETNZJY.cjs.map → chunk-2DBSVKAU.cjs.map} +1 -1
  4. package/dist/{chunk-O357OJXL.cjs → chunk-3YWVYA42.cjs} +18 -14
  5. package/dist/chunk-3YWVYA42.cjs.map +1 -0
  6. package/dist/{chunk-GAUZYBXE.cjs → chunk-5AAPD5WV.cjs} +11 -11
  7. package/dist/chunk-5AAPD5WV.cjs.map +1 -0
  8. package/dist/{chunk-SGEYDV3K.mjs → chunk-BDCX6LRQ.mjs} +3 -3
  9. package/dist/chunk-BDCX6LRQ.mjs.map +1 -0
  10. package/dist/{chunk-KZ5NGFUP.mjs → chunk-C3YNVHBP.mjs} +7 -20
  11. package/dist/chunk-C3YNVHBP.mjs.map +1 -0
  12. package/dist/{chunk-QXD7WVKK.mjs → chunk-CSTYBKZC.mjs} +1 -1
  13. package/dist/chunk-CSTYBKZC.mjs.map +1 -0
  14. package/dist/{chunk-SWG7CNTZ.mjs → chunk-CTSZ5D7S.mjs} +5 -9
  15. package/dist/chunk-CTSZ5D7S.mjs.map +1 -0
  16. package/dist/chunk-EW54H7CR.cjs +722 -0
  17. package/dist/chunk-EW54H7CR.cjs.map +1 -0
  18. package/dist/chunk-EX3NFXDK.mjs +722 -0
  19. package/dist/chunk-EX3NFXDK.mjs.map +1 -0
  20. package/dist/{chunk-A74HCO6T.cjs → chunk-G7XZSN2Q.cjs} +18 -31
  21. package/dist/chunk-G7XZSN2Q.cjs.map +1 -0
  22. package/dist/{chunk-RBIQT75L.mjs → chunk-GG3IX52W.mjs} +2 -2
  23. package/dist/{chunk-SLGOHIBL.mjs → chunk-IGK2AP6J.mjs} +17 -13
  24. package/dist/chunk-IGK2AP6J.mjs.map +1 -0
  25. package/dist/{chunk-VV7ELNRW.cjs → chunk-JMUK3K47.cjs} +3 -3
  26. package/dist/{chunk-VV7ELNRW.cjs.map → chunk-JMUK3K47.cjs.map} +1 -1
  27. package/dist/{chunk-SGCYEEXD.mjs → chunk-MNDVYPEP.mjs} +2 -9
  28. package/dist/chunk-MNDVYPEP.mjs.map +1 -0
  29. package/dist/{chunk-BPCGXXMD.cjs → chunk-NFUPPCX4.cjs} +18 -22
  30. package/dist/chunk-NFUPPCX4.cjs.map +1 -0
  31. package/dist/{chunk-PRCZMOLD.cjs → chunk-OPPYEA7F.cjs} +3 -3
  32. package/dist/{chunk-PRCZMOLD.cjs.map → chunk-OPPYEA7F.cjs.map} +1 -1
  33. package/dist/{chunk-7YKH4LZN.cjs → chunk-PYJMJF44.cjs} +2 -9
  34. package/dist/chunk-PYJMJF44.cjs.map +1 -0
  35. package/dist/{chunk-QA5AL3VZ.mjs → chunk-QHM7XNRK.mjs} +2 -2
  36. package/dist/{chunk-QA5AL3VZ.mjs.map → chunk-QHM7XNRK.mjs.map} +1 -1
  37. package/dist/codegen.cjs.map +1 -1
  38. package/dist/codegen.d.cts +7 -40
  39. package/dist/codegen.d.ts +7 -40
  40. package/dist/codegen.mjs.map +1 -1
  41. package/dist/{constants-BAimnqOK.d.cts → constants-DJ_KgZpN.d.cts} +8 -41
  42. package/dist/{constants-BAimnqOK.d.ts → constants-DJ_KgZpN.d.ts} +8 -41
  43. package/dist/{context-Cr93B05o.d.ts → context-Dt6xh_5t.d.cts} +4 -5
  44. package/dist/{context-DJ3REtdT.d.cts → context-Ml7k_V14.d.ts} +4 -5
  45. package/dist/{errors-Cqh0wrqt.d.ts → errors-COsBh7kd.d.cts} +2 -3
  46. package/dist/{errors-Cqh0wrqt.d.cts → errors-COsBh7kd.d.ts} +2 -3
  47. package/dist/handler-CZIlZ5vu.d.ts +227 -0
  48. package/dist/handler-kU9Jf36K.d.cts +227 -0
  49. package/dist/host.cjs +4 -4
  50. package/dist/host.d.cts +4 -4
  51. package/dist/host.d.ts +4 -4
  52. package/dist/host.mjs +3 -3
  53. package/dist/index.cjs +34 -5
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.cts +627 -69
  56. package/dist/index.d.ts +627 -69
  57. package/dist/index.mjs +33 -4
  58. package/dist/manifest.cjs +23 -132
  59. package/dist/manifest.cjs.map +1 -1
  60. package/dist/manifest.d.cts +12 -93
  61. package/dist/manifest.d.ts +12 -93
  62. package/dist/manifest.mjs +9 -118
  63. package/dist/manifest.mjs.map +1 -1
  64. package/dist/server.cjs +66 -14
  65. package/dist/server.cjs.map +1 -1
  66. package/dist/server.d.cts +6 -6
  67. package/dist/server.d.ts +6 -6
  68. package/dist/server.mjs +61 -9
  69. package/dist/server.mjs.map +1 -1
  70. package/dist/styles.css +367 -0
  71. package/dist/testing/index.cjs +13 -72
  72. package/dist/testing/index.cjs.map +1 -1
  73. package/dist/testing/index.d.cts +10 -29
  74. package/dist/testing/index.d.ts +10 -29
  75. package/dist/testing/index.mjs +13 -72
  76. package/dist/testing/index.mjs.map +1 -1
  77. package/dist/testing/jest.cjs +5 -5
  78. package/dist/testing/jest.mjs +2 -2
  79. package/dist/testing/vitest.cjs +5 -5
  80. package/dist/testing/vitest.cjs.map +1 -1
  81. package/dist/testing/vitest.d.cts +0 -10
  82. package/dist/testing/vitest.d.ts +0 -10
  83. package/dist/testing/vitest.mjs +2 -2
  84. package/dist/testing/vitest.mjs.map +1 -1
  85. package/dist/{types-BYGcp5pZ.d.ts → types-DDAGLpov.d.ts} +3 -4
  86. package/dist/{types-I1JOZyI_.d.cts → types-DrpQSv_2.d.cts} +3 -4
  87. package/dist/vite-preview.cjs +125 -294
  88. package/dist/vite-preview.cjs.map +1 -1
  89. package/dist/vite-preview.d.cts +24 -6
  90. package/dist/vite-preview.d.ts +24 -6
  91. package/dist/vite-preview.mjs +118 -287
  92. package/dist/vite-preview.mjs.map +1 -1
  93. package/dist/vite.cjs +23 -133
  94. package/dist/vite.cjs.map +1 -1
  95. package/dist/vite.d.cts +7 -40
  96. package/dist/vite.d.ts +7 -40
  97. package/dist/vite.mjs +17 -127
  98. package/dist/vite.mjs.map +1 -1
  99. package/package.json +2 -12
  100. package/schema/extension-v2.json +121 -432
  101. package/dist/chunk-2CY5HVY7.mjs +0 -59
  102. package/dist/chunk-2CY5HVY7.mjs.map +0 -1
  103. package/dist/chunk-7YKH4LZN.cjs.map +0 -1
  104. package/dist/chunk-A74HCO6T.cjs.map +0 -1
  105. package/dist/chunk-BPCGXXMD.cjs.map +0 -1
  106. package/dist/chunk-EB5XWQAU.cjs +0 -132
  107. package/dist/chunk-EB5XWQAU.cjs.map +0 -1
  108. package/dist/chunk-GAUZYBXE.cjs.map +0 -1
  109. package/dist/chunk-KZ5NGFUP.mjs.map +0 -1
  110. package/dist/chunk-O357OJXL.cjs.map +0 -1
  111. package/dist/chunk-ONJMPO3P.cjs +0 -59
  112. package/dist/chunk-ONJMPO3P.cjs.map +0 -1
  113. package/dist/chunk-QJMZRV7A.mjs +0 -132
  114. package/dist/chunk-QJMZRV7A.mjs.map +0 -1
  115. package/dist/chunk-QXD7WVKK.mjs.map +0 -1
  116. package/dist/chunk-SGCYEEXD.mjs.map +0 -1
  117. package/dist/chunk-SGEYDV3K.mjs.map +0 -1
  118. package/dist/chunk-SLGOHIBL.mjs.map +0 -1
  119. package/dist/chunk-SWG7CNTZ.mjs.map +0 -1
  120. package/dist/client-CV1qzQgx.d.cts +0 -25
  121. package/dist/client-CnlSHDWE.d.ts +0 -25
  122. package/dist/ui/styles.css +0 -740
  123. package/dist/ui.cjs +0 -1018
  124. package/dist/ui.cjs.map +0 -1
  125. package/dist/ui.d.cts +0 -231
  126. package/dist/ui.d.ts +0 -231
  127. package/dist/ui.mjs +0 -1018
  128. package/dist/ui.mjs.map +0 -1
  129. package/dist/vite-preview-bootstrap.cjs +0 -66
  130. package/dist/vite-preview-bootstrap.cjs.map +0 -1
  131. package/dist/vite-preview-bootstrap.d.cts +0 -15
  132. package/dist/vite-preview-bootstrap.d.ts +0 -15
  133. package/dist/vite-preview-bootstrap.mjs +0 -66
  134. package/dist/vite-preview-bootstrap.mjs.map +0 -1
  135. package/dist/work-events-DB3WR468.d.ts +0 -547
  136. package/dist/work-events-Dxe7Djdy.d.cts +0 -547
  137. /package/dist/{chunk-RBIQT75L.mjs.map → chunk-GG3IX52W.mjs.map} +0 -0
@@ -3,15 +3,15 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkA74HCO6Tcjs = require('./chunk-A74HCO6T.cjs');
6
+ var _chunkG7XZSN2Qcjs = require('./chunk-G7XZSN2Q.cjs');
7
7
 
8
8
 
9
9
 
10
- var _chunk7YKH4LZNcjs = require('./chunk-7YKH4LZN.cjs');
10
+ var _chunkPYJMJF44cjs = require('./chunk-PYJMJF44.cjs');
11
11
 
12
12
 
13
13
 
14
- var _chunk6HETNZJYcjs = require('./chunk-6HETNZJY.cjs');
14
+ var _chunk2DBSVKAUcjs = require('./chunk-2DBSVKAU.cjs');
15
15
 
16
16
  // src/testing/fixtures.ts
17
17
  var fixtureApps = [
@@ -274,16 +274,16 @@ async function readEnvelopeResponse(response, call) {
274
274
  if (response.ok) {
275
275
  if (body.parsed) return body.value;
276
276
  if (text.trim() === "") return void 0;
277
- throw new (0, _chunk6HETNZJYcjs.TapServerError)(
278
- _chunkA74HCO6Tcjs.SERVER_ERROR_CODES.INTERNAL_ERROR,
277
+ throw new (0, _chunk2DBSVKAUcjs.TapServerError)(
278
+ _chunkG7XZSN2Qcjs.SERVER_ERROR_CODES.INTERNAL_ERROR,
279
279
  `The extension server answered ${status} for ${call} with a body that is not JSON. Every Tappify route answers JSON, or 204 with no body.`,
280
280
  response.status
281
281
  );
282
282
  }
283
- const error = _chunkA74HCO6Tcjs.readRecord.call(void 0, body.value, "error");
284
- throw new (0, _chunk6HETNZJYcjs.TapServerError)(
285
- _nullishCoalesce(_chunkA74HCO6Tcjs.readText.call(void 0, error, "code"), () => ( _chunkA74HCO6Tcjs.SERVER_ERROR_CODES.INTERNAL_ERROR)),
286
- _nullishCoalesce(_chunkA74HCO6Tcjs.readText.call(void 0, error, "message"), () => ( `The extension server answered ${status} for ${call} without a JSON error body.`)),
283
+ const error = _chunkG7XZSN2Qcjs.readRecord.call(void 0, body.value, "error");
284
+ throw new (0, _chunk2DBSVKAUcjs.TapServerError)(
285
+ _nullishCoalesce(_chunkG7XZSN2Qcjs.readText.call(void 0, error, "code"), () => ( _chunkG7XZSN2Qcjs.SERVER_ERROR_CODES.INTERNAL_ERROR)),
286
+ _nullishCoalesce(_chunkG7XZSN2Qcjs.readText.call(void 0, error, "message"), () => ( `The extension server answered ${status} for ${call} without a JSON error body.`)),
287
287
  response.status
288
288
  );
289
289
  }
@@ -323,14 +323,14 @@ async function signTestToken(claims = {}, options = {}) {
323
323
  var HEALTH_PATH = "/tappify/health";
324
324
  function createTestClient(target, defaults = {}) {
325
325
  if (typeof target === "function") {
326
- throw new (0, _chunk6HETNZJYcjs.TapError)(
326
+ throw new (0, _chunk2DBSVKAUcjs.TapError)(
327
327
  "TAP_TEST_TARGET_INVALID",
328
328
  "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."
329
329
  );
330
330
  }
331
331
  const extensionId = target.extensionId;
332
332
  const handlerPromise = testJwks().then(
333
- (jwks) => _chunkA74HCO6Tcjs.createTappifyHandler.call(void 0, { ...target, jwks })
333
+ (jwks) => _chunkG7XZSN2Qcjs.createTappifyHandler.call(void 0, { ...target, jwks })
334
334
  );
335
335
  const install = {
336
336
  id: "ins_test",
@@ -404,11 +404,7 @@ function createTestClient(target, defaults = {}) {
404
404
  event: async (name, payload) => {
405
405
  await call("POST", "/tappify/events", envelope({}, { name, payload }));
406
406
  },
407
- work: (async (operation, input) => call(
408
- "POST",
409
- `/tappify/work/${operation}`,
410
- envelope(input)
411
- )),
407
+ work: (operation, input) => call("POST", `/tappify/work/${operation}`, envelope(input)),
412
408
  raw: (method, path, body) => send(method, path, body === void 0 ? void 0 : JSON.stringify(body))
413
409
  };
414
410
  }
@@ -428,7 +424,7 @@ var PROBE_KEYS = /* @__PURE__ */ new Set([
428
424
  "nodeType"
429
425
  ]);
430
426
  function unknownCollectionError(collection) {
431
- return new (0, _chunk6HETNZJYcjs.TapError)(
427
+ return new (0, _chunk2DBSVKAUcjs.TapError)(
432
428
  "TAP_UNKNOWN_COLLECTION",
433
429
  `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.`
434
430
  );
@@ -449,7 +445,7 @@ function collectionView(handle) {
449
445
  };
450
446
  }
451
447
  function scopeError(scope) {
452
- return new (0, _chunk6HETNZJYcjs.TapError)(
448
+ return new (0, _chunk2DBSVKAUcjs.TapError)(
453
449
  "TAP_SCOPE_MISSING",
454
450
  `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.`
455
451
  );
@@ -458,7 +454,7 @@ function asObject(value) {
458
454
  return typeof value === "object" && value !== null ? value : {};
459
455
  }
460
456
  function createTapMock(options = {}) {
461
- const granted = new Set(_nullishCoalesce(options.scopes, () => ( _chunk7YKH4LZNcjs.SCOPE_KEYS)));
457
+ const granted = new Set(_nullishCoalesce(options.scopes, () => ( _chunkPYJMJF44cjs.SCOPE_KEYS)));
462
458
  const listeners = /* @__PURE__ */ new Map();
463
459
  const eventHandlers = /* @__PURE__ */ new Map();
464
460
  const state = /* @__PURE__ */ new Map();
@@ -607,7 +603,7 @@ function createTapMock(options = {}) {
607
603
  );
608
604
  return readEnvelopeResponse(response, `POST ${path}`);
609
605
  }
610
- throw new (0, _chunk6HETNZJYcjs.TapError)(
606
+ throw new (0, _chunk2DBSVKAUcjs.TapError)(
611
607
  "TAP_UNKNOWN_PROCEDURE",
612
608
  `No handler is registered for the procedure "${name}". Pass server: { ${name}: input => \u2026 } or handler: <your createTappifyHandler handler> to createTapMock().`
613
609
  );
@@ -662,7 +658,7 @@ function createTapMock(options = {}) {
662
658
  },
663
659
  data: {
664
660
  query: (query) => {
665
- const scope = _chunk7YKH4LZNcjs.QUERY_SCOPES[query.kind];
661
+ const scope = _chunkPYJMJF44cjs.QUERY_SCOPES[query.kind];
666
662
  if (!granted.has(scope)) return Promise.reject(scopeError(scope));
667
663
  return Promise.resolve(answerFixtureQuery(query));
668
664
  },
@@ -778,4 +774,4 @@ function createTapMock(options = {}) {
778
774
 
779
775
 
780
776
  exports.fixtureApps = fixtureApps; exports.fixtureReleases = fixtureReleases; exports.fixtureKeywords = fixtureKeywords; exports.fixtureListings = fixtureListings; exports.fixtureProject = fixtureProject; exports.fixtureSeries = fixtureSeries; exports.fixtureReviews = fixtureReviews; exports.fixtureCrashes = fixtureCrashes; exports.fixtureRevenue = fixtureRevenue; exports.fixtures = fixtures; exports.answerFixtureQuery = answerFixtureQuery; exports.testJwks = testJwks; exports.signTestToken = signTestToken; exports.createTestClient = createTestClient; exports.createTapMock = createTapMock;
781
- //# sourceMappingURL=chunk-BPCGXXMD.cjs.map
777
+ //# sourceMappingURL=chunk-NFUPPCX4.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/chunk-NFUPPCX4.cjs","../src/testing/fixtures.ts","../src/testing/client.ts","../src/testing/envelope.ts","../src/testing/mock.ts"],"names":[],"mappings":"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","file":"/home/runner/work/extension-sdk/extension-sdk/dist/chunk-NFUPPCX4.cjs","sourcesContent":[null,"import type {\n TapApp,\n TapCrashesResult,\n TapKeyword,\n TapListing,\n TapProjectResult,\n TapQuery,\n TapRelease,\n TapResult,\n TapRevenueResult,\n TapReviewsResult,\n TapSeriesMetric,\n TapSeriesPoint,\n TapSeriesResult,\n} from '../client/types';\n\n/** The owner's two Northlight apps, one per platform. */\nexport const fixtureApps: TapApp[] = [\n {\n id: 'app_ios',\n name: 'Northlight',\n platform: 'ios',\n storeId: '1490000001',\n bundleId: 'ai.tappify.northlight',\n },\n {\n id: 'app_android',\n name: 'Northlight',\n platform: 'android',\n storeId: 'ai.tappify.northlight',\n bundleId: 'ai.tappify.northlight',\n },\n];\n\n/** Three shipped releases: two iOS versions and the Android build of the newer one. */\nexport const fixtureReleases: TapRelease[] = [\n {\n id: 'rel_42',\n version: '4.2.0',\n build: '118',\n platform: 'ios',\n state: 'ready_for_sale',\n shippedAt: '2026-09-01T09:00:00.000Z',\n },\n {\n id: 'rel_41',\n version: '4.1.0',\n build: '107',\n platform: 'ios',\n state: 'ready_for_sale',\n shippedAt: '2026-08-11T09:00:00.000Z',\n },\n {\n id: 'rel_a42',\n version: '4.2.0',\n build: '4200',\n platform: 'android',\n state: 'published',\n shippedAt: '2026-09-02T09:00:00.000Z',\n },\n];\n\n/**\n * Three tracked terms with positions, two on iOS and one on Android, scored on\n * the host's 1 to 10 traffic scale.\n */\nexport const fixtureKeywords: TapKeyword[] = [\n {\n id: 'kw_1',\n term: 'photo editor',\n platform: 'ios',\n country: 'US',\n position: 4,\n popularity: 6.6,\n },\n {\n id: 'kw_2',\n term: 'collage maker',\n platform: 'ios',\n country: 'US',\n position: 11,\n popularity: 5.3,\n },\n {\n id: 'kw_3',\n term: 'photo editor',\n platform: 'android',\n country: 'US',\n position: 7,\n popularity: 6,\n },\n];\n\n/** One store listing, for iOS in `en-US`. */\nexport const fixtureListings: TapListing[] = [\n {\n platform: 'ios',\n country: 'US',\n locale: 'en-US',\n title: 'Northlight — Photo Editor',\n subtitle: 'Edit, collage, share',\n description: 'Northlight turns a camera roll into a finished post.',\n keywords: ['photo', 'editor', 'collage'],\n updatedAt: '2026-08-28T12:00:00.000Z',\n },\n];\n\n/** The Northlight project, with `fixtureApps` and `fixtureReleases` under it. */\nexport const fixtureProject: TapProjectResult = {\n project: {\n id: 'prj_north',\n name: 'Northlight',\n platforms: ['ios', 'android'],\n },\n apps: fixtureApps,\n releases: fixtureReleases,\n};\n\nconst SERIES_VALUES: Record<TapSeriesMetric, number[]> = {\n downloads: [1180, 1240, 1310, 1290, 1402, 1512, 1488],\n impressions: [21400, 22150, 22980, 22110, 24020, 25510, 25010],\n page_views: [8120, 8340, 8710, 8290, 9110, 9640, 9420],\n conversion_rate: [0.145, 0.149, 0.15, 0.156, 0.152, 0.157, 0.158],\n};\n\nfunction week(): string[] {\n return [\n '2026-09-01',\n '2026-09-02',\n '2026-09-03',\n '2026-09-04',\n '2026-09-05',\n '2026-09-06',\n '2026-09-07',\n ];\n}\n\n/**\n * Builds the seven-day series for one metric, over the first week of September\n * 2026.\n *\n * @remarks\n * The same seven values every call, so an assertion on a total or a peak is\n * stable: downloads peak at 1,512 and `conversion_rate` comes back with unit\n * `ratio` where the other three come back as `count`. The range in the query does\n * not change the points, so a test that drives the filter bar asserts on the call,\n * not on different numbers.\n *\n * @example\n * ```ts\n * import { fixtureSeries } from '@tappify/extension-sdk/testing';\n *\n * const downloads = fixtureSeries('downloads');\n * const best = Math.max(...downloads.points.map(point => point.value));\n * ```\n */\nexport function fixtureSeries(metric: TapSeriesMetric): TapSeriesResult {\n const points: TapSeriesPoint[] = week().map((at, index) => ({\n at,\n value: SERIES_VALUES[metric][index],\n }));\n\n return {\n metric,\n unit: metric === 'conversion_rate' ? 'ratio' : 'count',\n points,\n };\n}\n\n/** Three reviews across three countries, averaging 3.7 stars, one already replied to. */\nexport const fixtureReviews: TapReviewsResult = {\n total: 3,\n averageRating: 3.7,\n reviews: [\n {\n id: 'rev_1',\n platform: 'ios',\n country: 'US',\n rating: 5,\n title: 'Fast and clean',\n body: 'The new collage layouts are exactly what I wanted.',\n author: 'kbrady',\n submittedAt: '2026-09-05T18:22:00.000Z',\n replied: false,\n },\n {\n id: 'rev_2',\n platform: 'ios',\n country: 'GB',\n rating: 2,\n title: 'Crashes on export',\n body: 'Since 4.2 the export button closes the app.',\n author: null,\n submittedAt: '2026-09-04T08:10:00.000Z',\n replied: true,\n },\n {\n id: 'rev_3',\n platform: 'android',\n country: 'DE',\n rating: 4,\n title: null,\n body: 'Gute App, aber der Export dauert lange.',\n author: 'mgruber',\n submittedAt: '2026-09-03T16:45:00.000Z',\n replied: false,\n },\n ],\n};\n\n/** A 99.42% crash-free user rate with two issues, both on build 118. */\nexport const fixtureCrashes: TapCrashesResult = {\n crashFreeRate: 99.42,\n issues: [\n {\n id: 'crash_1',\n title: 'ExportSession.finish — index out of range',\n count: 214,\n build: '118',\n firstSeenAt: '2026-09-01T11:02:00.000Z',\n lastSeenAt: '2026-09-07T21:40:00.000Z',\n },\n {\n id: 'crash_2',\n title: 'PhotoLibrary.load — permission revoked',\n count: 37,\n build: '118',\n firstSeenAt: '2026-09-02T07:15:00.000Z',\n lastSeenAt: '2026-09-06T13:05:00.000Z',\n },\n ],\n};\n\n/** 41,200 USD of recurring revenue, 18 refunds, and a seven-day series under it. */\nexport const fixtureRevenue: TapRevenueResult = {\n currency: 'USD',\n mrr: 41200,\n refunds: 18,\n points: week().map((at, index) => ({\n at,\n value: [1310, 1355, 1401, 1388, 1470, 1525, 1502][index],\n })),\n};\n\n/** The shape of `fixtures`: every sandbox fixture under one name. */\nexport interface TapFixtures {\n project: TapProjectResult;\n apps: TapApp[];\n releases: TapRelease[];\n keywords: TapKeyword[];\n listings: TapListing[];\n reviews: TapReviewsResult;\n crashes: TapCrashesResult;\n revenue: TapRevenueResult;\n /** `fixtureSeries`, so a series is built per metric rather than held. */\n series(metric: TapSeriesMetric): TapSeriesResult;\n}\n\n/**\n * The sandbox data the mock bridge and the local preview both answer with, so a\n * component behaves the same in a test and in the preview.\n *\n * @remarks\n * These are the same objects the `fixture*` exports hold, gathered under one name.\n * `answerFixtureQuery` clones before it answers, but reading `fixtures` directly\n * hands you the live object, so a test that sorts or splices one has to copy it\n * first.\n *\n * @example\n * ```ts\n * import { fixtures } from '@tappify/extension-sdk/testing';\n *\n * const newest = [...fixtures.releases].sort((left, right) =>\n * right.version.localeCompare(left.version),\n * )[0];\n * ```\n */\nexport const fixtures: TapFixtures = {\n project: fixtureProject,\n apps: fixtureApps,\n releases: fixtureReleases,\n keywords: fixtureKeywords,\n listings: fixtureListings,\n reviews: fixtureReviews,\n crashes: fixtureCrashes,\n revenue: fixtureRevenue,\n series: fixtureSeries,\n};\n\n/**\n * Answers one `tap.data.query` from the fixtures, which is what the mock bridge\n * does.\n *\n * @remarks\n * Every branch is cloned with `structuredClone`, so a component that sorts or\n * splices a result cannot reach the fixture the next test reads. It checks no\n * scope — `createTapMock` refuses an ungranted kind before it gets here — and it\n * ignores the range and platform in the query, so the numbers are the same\n * whatever the filter bar says. The seven assertions inside are the one place\n * TypeScript cannot follow the distributive conditional in `TapResult<Q>` back\n * through a `switch`; each branch returns exactly the result its `case` selected.\n *\n * @example\n * ```ts\n * import { answerFixtureQuery } from '@tappify/extension-sdk/testing';\n *\n * const reviews = answerFixtureQuery({\n * kind: 'reviews',\n * range: { from: '2026-09-01', to: '2026-09-08' },\n * });\n * const lowest = Math.min(...reviews.reviews.map(review => review.rating));\n * ```\n */\nexport function answerFixtureQuery<Q extends TapQuery>(query: Q): TapResult<Q> {\n switch (query.kind) {\n case 'project':\n return structuredClone(fixtureProject) as TapResult<Q>;\n case 'series':\n return structuredClone(fixtureSeries(query.metric)) as TapResult<Q>;\n case 'keywords':\n return structuredClone({ keywords: fixtureKeywords }) as TapResult<Q>;\n case 'listing':\n return structuredClone({ listings: fixtureListings }) as TapResult<Q>;\n case 'reviews':\n return structuredClone(fixtureReviews) as TapResult<Q>;\n case 'crashes':\n return structuredClone(fixtureCrashes) as TapResult<Q>;\n case 'revenue':\n return structuredClone(fixtureRevenue) as TapResult<Q>;\n }\n}\n","import {\n exportJWK,\n generateKeyPair,\n SignJWT,\n type CryptoKey,\n type JSONWebKeySet,\n} from 'jose';\nimport { TapError } from '../client/errors';\nimport type { TapFilters } from '../client/types';\nimport { createTappifyHandler } from '../server/handler';\nimport type {\n ContextBlock,\n MentionItems,\n MetricsInput,\n MetricsResponse,\n TappifyClaims,\n TappifyHandlerOptions,\n TappifyHealth,\n TappifyInstall,\n WorkOperation,\n} from '../server/types';\nimport {\n callEnvelope,\n DEFAULT_FILTERS,\n readEnvelopeResponse,\n} from './envelope';\n\nconst TEST_KID = 'tappify-test-key';\nconst TEST_ISSUER = 'https://api.tappify.ai';\n\ninterface TestKeys {\n privateKey: CryptoKey;\n jwks: JSONWebKeySet;\n}\n\nlet keys: Promise<TestKeys> | null = null;\n\nfunction ensureKeys(): Promise<TestKeys> {\n keys ??= (async () => {\n const pair = await generateKeyPair('RS256', { extractable: true });\n const publicJwk = await exportJWK(pair.publicKey);\n return {\n privateKey: pair.privateKey,\n jwks: {\n keys: [{ ...publicJwk, kid: TEST_KID, alg: 'RS256', use: 'sig' }],\n },\n };\n })();\n\n return keys;\n}\n\n/**\n * Returns the public key set that verifies the tokens `signTestToken` signs.\n *\n * @remarks\n * The pair is generated once per process and cached, so every call in a suite\n * returns the same set. Pass it as `jwks` to `createTappifyHandler` when a test\n * drives the handler itself; `createTestClient` does that for you.\n *\n * @example\n * ```ts\n * import { createTappifyHandler } from '@tappify/extension-sdk/server';\n * import { testJwks } from '@tappify/extension-sdk/testing';\n *\n * async function buildHandler() {\n * return createTappifyHandler({ extensionId: 'starter', jwks: await testJwks() });\n * }\n * ```\n */\nexport async function testJwks(): Promise<JSONWebKeySet> {\n return (await ensureKeys()).jwks;\n}\n\n/**\n * Signs an install token a handler built against `testJwks` accepts.\n *\n * @remarks\n * Every claim has a default — install `ins_test`, extension `starter`, project\n * `prj_north`, organization `org_test`, no scopes — and the audience is\n * `ext:<extensionId>`, taken from `options.extensionId`, then from the claims, then\n * `starter`. It expires in five minutes unless `expiresIn` says otherwise, which is\n * how a test covers an expired token. `createTestClient` and `createTapMock` sign\n * their own tokens, so reach for this only when a test verifies a token itself.\n *\n * @example\n * ```ts\n * import { verifyTappifyToken } from '@tappify/extension-sdk/server';\n * import { signTestToken, testJwks } from '@tappify/extension-sdk/testing';\n *\n * async function claimsOfTestToken() {\n * const token = await signTestToken({ scopes: ['analytics:read'] });\n * return verifyTappifyToken(token, {\n * extensionId: 'starter',\n * jwks: await testJwks(),\n * });\n * }\n * ```\n */\nexport async function signTestToken(\n claims: Partial<TappifyClaims> = {},\n options: { extensionId?: string; expiresIn?: string } = {},\n): Promise<string> {\n const { privateKey } = await ensureKeys();\n const extensionId = options.extensionId ?? claims.extensionId ?? 'starter';\n\n return new SignJWT({\n installId: claims.installId ?? 'ins_test',\n extensionId,\n projectId: claims.projectId ?? 'prj_north',\n organizationId: claims.organizationId ?? 'org_test',\n scopes: claims.scopes ?? [],\n ...(claims.userId === undefined ? {} : { userId: claims.userId }),\n })\n .setProtectedHeader({ alg: 'RS256', kid: TEST_KID })\n .setIssuer(TEST_ISSUER)\n .setAudience(`ext:${extensionId}`)\n .setIssuedAt()\n .setExpirationTime(options.expiresIn ?? '5m')\n .sign(privateKey);\n}\n\n/** What every call a `TestClient` makes carries, unless a call overrides it. */\nexport interface TestClientDefaults {\n /** Merged over the default install, whose id is `ins_test`. */\n install?: Partial<TappifyInstall>;\n /** The scopes the token claims. Defaults to none. */\n scopes?: string[];\n /** What the handler reads as `credentials`. Defaults to empty. */\n credentials?: Record<string, unknown>;\n /** What the handler reads as `documents`, including `settings`. */\n documents?: Record<string, unknown>;\n /** The filter bar behind the call. Defaults to a seven-day September range. */\n filters?: TapFilters;\n /** One fixed idempotency key. Left out, each call gets its own. */\n eventId?: string;\n /** The origin the request url is built on. Defaults to `https://vendor.test`. */\n origin?: string;\n}\n\n/** One method per route Tappify calls, plus `raw` for the response itself. */\nexport interface TestClient {\n /** Calls the health route, which carries no token. */\n health(): Promise<TappifyHealth>;\n /** Calls the connector's metrics route. */\n metrics(input: MetricsInput): Promise<MetricsResponse>;\n /** Calls one tool by id and returns the card it answered with. */\n tool(id: string, input: unknown): Promise<unknown>;\n /** Calls one mention by id, with the query as the `q` parameter. */\n mention(id: string, query: string): Promise<MentionItems>;\n /** Calls one action by id. */\n action(id: string, input: unknown): Promise<unknown>;\n /** Calls one procedure by name; the input defaults to an empty object. */\n procedure(name: string, input?: unknown): Promise<unknown>;\n /** Calls one context block by id. */\n context(id: string): Promise<ContextBlock>;\n /** Posts one Tappify event, which the handler answers with 204. */\n event(name: string, payload: Record<string, unknown>): Promise<void>;\n /** Calls one work operation. */\n work(operation: WorkOperation, input: unknown): Promise<unknown>;\n /**\n * Sends any method and path and returns the `Response`, for a test that asserts\n * on a status rather than a body.\n */\n raw(method: string, path: string, body?: unknown): Promise<Response>;\n}\n\nconst HEALTH_PATH = '/tappify/health';\n\n/**\n * Drives a vendor server the way Tappify does, so a route is tested without a\n * running Tappify.\n *\n * @remarks\n * Pass the options object you hand `createTappifyHandler`, not the handler it\n * returns: the client rebuilds the handler around `testJwks` and signs each call\n * with a matching token. A built handler throws a `TapError` carrying\n * `TAP_TEST_TARGET_INVALID`, because it would verify against the live Tappify key\n * set and answer 401. Every call carries a fresh event id and the envelope Tappify\n * sends, and every call but `health` a signed token, so the handler reads the same\n * `install`, `context`, `credentials` and `documents` it will read in production. A route that\n * answers an error rejects with a `TapServerError` carrying the code, message and\n * status from the body, which is what a test asserts on; use `raw` when the status\n * itself is the assertion. Every method calls the unprefixed path — `/tappify/…` —\n * whatever `basePath` the options carry, so a `basePath` the handler strips off\n * something else is not exercised here, and one that is itself a prefix of\n * `/tappify`, such as `/tap`, makes every call 404 `TAP_ROUTE_UNKNOWN`. Reach a\n * prefixed path through `raw` when that is what you need to cover.\n *\n * @example\n * ```ts\n * import type { TappifyHandlerOptions } from '@tappify/extension-sdk/server';\n * import { createTestClient } from '@tappify/extension-sdk/testing';\n * import { expect, it } from 'vitest';\n *\n * const handlerOptions: TappifyHandlerOptions = {\n * extensionId: 'starter',\n * health: () => ({ ok: true, version: '0.1.0' }),\n * };\n * const client = createTestClient(handlerOptions, { scopes: ['analytics:read'] });\n *\n * it('answers the health check', async () => {\n * await expect(client.health()).resolves.toEqual({ ok: true, version: '0.1.0' });\n * });\n * ```\n */\nexport function createTestClient(\n target: TappifyHandlerOptions,\n defaults: TestClientDefaults = {},\n): TestClient {\n if (typeof target === 'function') {\n throw new TapError(\n 'TAP_TEST_TARGET_INVALID',\n '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 — createTestClient(handlerOptions) — 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.',\n );\n }\n\n const extensionId = target.extensionId;\n const handlerPromise = testJwks().then(jwks =>\n createTappifyHandler({ ...target, jwks }),\n );\n\n const install: TappifyInstall = {\n id: 'ins_test',\n extensionId,\n projectId: 'prj_north',\n organizationId: 'org_test',\n ...defaults.install,\n };\n\n const filters = defaults.filters ?? DEFAULT_FILTERS;\n const origin = defaults.origin ?? 'https://vendor.test';\n\n let sent = 0;\n\n function envelope(input: unknown, event?: unknown): string {\n return callEnvelope({\n install,\n filters,\n input,\n credentials: defaults.credentials,\n documents: defaults.documents,\n event,\n });\n }\n\n async function send(\n method: string,\n path: string,\n body?: string,\n ): Promise<Response> {\n const handler = await handlerPromise;\n sent += 1;\n const headers = new Headers({\n 'content-type': 'application/json',\n 'x-tappify-event-id': defaults.eventId ?? `evt_${path}_${String(sent)}`,\n });\n\n if (path !== HEALTH_PATH) {\n const token = await signTestToken(\n {\n installId: install.id,\n extensionId: install.extensionId,\n projectId: install.projectId,\n organizationId: install.organizationId,\n scopes: defaults.scopes ?? [],\n },\n { extensionId },\n );\n headers.set('authorization', `Bearer ${token}`);\n }\n\n return handler(\n new Request(`${origin}${path}`, {\n method,\n headers,\n ...(body === undefined ? {} : { body }),\n }),\n );\n }\n\n async function call(\n method: string,\n path: string,\n body?: string,\n ): Promise<unknown> {\n return readEnvelopeResponse(\n await send(method, path, body),\n `${method} ${path}`,\n );\n }\n\n return {\n health: async () => (await call('GET', HEALTH_PATH)) as TappifyHealth,\n metrics: async input =>\n (await call(\n 'POST',\n '/tappify/metrics',\n envelope(input),\n )) as MetricsResponse,\n tool: (id, input) => call('POST', `/tappify/tools/${id}`, envelope(input)),\n mention: async (id, query) =>\n (await call(\n 'GET',\n `/tappify/mentions/${id}?q=${encodeURIComponent(query)}`,\n )) as MentionItems,\n action: (id, input) =>\n call('POST', `/tappify/actions/${id}`, envelope(input)),\n procedure: (name, input = {}) =>\n call('POST', `/tappify/procedures/${name}`, envelope(input)),\n context: async id =>\n (await call('GET', `/tappify/context/${id}`)) as ContextBlock,\n event: async (name, payload) => {\n await call('POST', '/tappify/events', envelope({}, { name, payload }));\n },\n work: (operation, input) =>\n call('POST', `/tappify/work/${operation}`, envelope(input)),\n raw: (method, path, body) =>\n send(method, path, body === undefined ? undefined : JSON.stringify(body)),\n };\n}\n","import { TapServerError } from '../client/errors';\nimport type { TapFilters } from '../client/types';\nimport { SERVER_ERROR_CODES } from '../server/codes';\nimport type { TappifyCallContext, TappifyInstall } from '../server/types';\nimport { readRecord, readText } from '../server/wire';\n\nexport const DEFAULT_FILTERS: TapFilters = {\n range: { from: '2026-09-01', to: '2026-09-08', preset: '7d' },\n platform: 'all',\n country: 'all',\n};\n\nexport interface CallEnvelope {\n install: TappifyInstall;\n filters: TapFilters;\n input: unknown;\n credentials?: Record<string, unknown>;\n documents?: Record<string, unknown>;\n event?: unknown;\n}\n\nexport function callEnvelope(envelope: CallEnvelope): string {\n const context: TappifyCallContext = {\n projectId: envelope.install.projectId,\n platform: envelope.filters.platform,\n from: envelope.filters.range.from,\n to: envelope.filters.range.to,\n filters: envelope.filters,\n };\n\n return JSON.stringify({\n install: envelope.install,\n context,\n input: envelope.input,\n credentials: envelope.credentials ?? {},\n documents: envelope.documents ?? {},\n event: envelope.event,\n });\n}\n\nfunction parseJson(text: string): { parsed: boolean; value: unknown } {\n try {\n return { parsed: true, value: JSON.parse(text) as unknown };\n } catch {\n return { parsed: false, value: undefined };\n }\n}\n\nexport async function readEnvelopeResponse(\n response: Response,\n call: string,\n): Promise<unknown> {\n const text = await response.text();\n const body = parseJson(text);\n const status = String(response.status);\n\n if (response.ok) {\n if (body.parsed) return body.value;\n if (text.trim() === '') return undefined;\n\n throw new TapServerError(\n SERVER_ERROR_CODES.INTERNAL_ERROR,\n `The extension server answered ${status} for ${call} with a body that is not JSON. Every Tappify route answers JSON, or 204 with no body.`,\n response.status,\n );\n }\n\n const error = readRecord(body.value, 'error');\n\n throw new TapServerError(\n readText(error, 'code') ?? SERVER_ERROR_CODES.INTERNAL_ERROR,\n readText(error, 'message') ??\n `The extension server answered ${status} for ${call} without a JSON error body.`,\n response.status,\n );\n}\n","import { TapError } from '../client/errors';\nimport type {\n Tap,\n TapChangeKey,\n TapChatCard,\n TapCollection,\n TapConfirmOptions,\n TapFilters,\n TapHostEvents,\n TapProcedures,\n TapSingleton,\n TapSize,\n TapStorage,\n TapToastTone,\n} from '../client/types';\nimport { QUERY_SCOPES, SCOPE_KEYS, type ScopeKey } from '../manifest/constants';\nimport type { TappifyFetchHandler } from '../server/handler';\nimport { signTestToken } from './client';\nimport {\n callEnvelope,\n DEFAULT_FILTERS,\n readEnvelopeResponse,\n} from './envelope';\nimport { answerFixtureQuery } from './fixtures';\n\n/**\n * Everything the component asked the host to do, in call order, for a test to\n * assert on.\n */\nexport interface TapMockCalls {\n /** One entry per `tap.nav.push`. */\n navigations: string[];\n /** One entry per `tap.nav.setSearch`. */\n searches: Record<string, string | null>[];\n /** One entry per `tap.ui.toast`; the tone defaults to `neutral`. */\n toasts: { message: string; tone: TapToastTone }[];\n /** One entry per `tap.ui.confirm`, whatever the mock answered. */\n confirms: TapConfirmOptions[];\n /** One entry per `tap.actions.run`, which the mock records rather than refuses. */\n actions: { actionId: string; input: unknown }[];\n /** One entry per `tap.telemetry.event`. */\n telemetry: {\n name: string;\n props?: Record<string, string | number | boolean>;\n }[];\n /** One entry per `tap.nav.openChat`. */\n chats: { prompt: string; context?: Record<string, unknown> }[];\n /** One url per `tap.nav.openExternal`, with no confirmation asked. */\n externals: string[];\n /** The filename and byte size per `tap.ui.download`, not the blob itself. */\n downloads: { filename: string; size: number }[];\n /** One entry per `tap.ui.copy`, with no clipboard touched. */\n copies: string[];\n /** One card per `tap.ui.openInChat`, unformatted. */\n cards: TapChatCard[];\n /** How many times `tap.ui.expand` was called. */\n expands: number;\n /** How many times `tap.nav.openSettings` was called. */\n settingsOpens: number;\n}\n\n/**\n * How the mock bridge is built, and the options `renderWithTap` forwards to it.\n */\nexport interface CreateTapMockOptions {\n /** Defaults to `ins_test`. */\n installId?: string;\n /** Defaults to `starter`, and has to match `handler`'s own extension id. */\n extensionId?: string;\n /** Defaults to `Starter`. */\n extensionName?: string;\n /** What the install granted. Defaults to every scope. */\n scopes?: ScopeKey[];\n /** What `tap.ui.size` reports. Defaults to `slot`. */\n size?: TapSize;\n /** Merged over the fixture filter bar. */\n filters?: Partial<TapFilters>;\n /** Merged over the fixture Northlight project. */\n project?: Partial<Tap['project']>;\n /** What `tap.context` holds. Defaults to empty. */\n context?: Record<string, unknown>;\n /** What `tap.params` holds, in the host's positional shape. Defaults to empty. */\n params?: Record<string, string>;\n /** What `useTapTheme` reports. Defaults to `light`. */\n theme?: 'light' | 'dark';\n /** What `tap.ui.confirm` resolves to. Defaults to `true`. */\n confirmAnswer?: boolean;\n /** The collections to expose, and the kind of each. Left out, every name works. */\n storage?: Record<string, 'singleton' | 'collection'>;\n /** One function per procedure, answering `tap.server.<name>` in process. */\n server?: Record<string, (input: unknown) => unknown>;\n /** Your own handler, so `tap.server.<name>` goes through its real routes. */\n handler?: TappifyFetchHandler;\n /** A token to send to `handler`. Defaults to one the mock signs itself. */\n handlerToken?: string;\n}\n\n/** The mock bridge, with the recorded calls and the handles that drive it. */\nexport interface TapMock {\n /** The bridge itself, which `renderWithTap` puts above the component. */\n tap: Tap;\n calls: TapMockCalls;\n /** The node `TapDialog` renders into, appended to the document by `renderWithTap`. */\n portal: HTMLElement;\n /** What a collection holds now, or just the one document under `documentId`. */\n documents(collection: string, documentId?: string): unknown[];\n /** Delivers a Tappify event to everything subscribed through `tap.data.subscribe`. */\n emit<E extends keyof TapHostEvents>(\n event: E,\n payload: TapHostEvents[E],\n ): void;\n /** Replaces the whole filter bar and re-renders the hooks that read it. */\n setFilters(filters: TapFilters): void;\n /** Moves the mount between slot, panel and page. */\n setSize(size: TapSize): void;\n /** Switches the theme `useTapTheme` reports. */\n setTheme(mode: 'light' | 'dark'): void;\n /** Replaces the route `useTapParams` reports, in the host's own positional shape. */\n setParams(params: Record<string, string>): void;\n}\n\ninterface StoredDocument {\n id: string;\n data: unknown;\n}\n\ntype StorageHandle = TapSingleton<unknown> & TapCollection<unknown>;\n\ntype ExposedHandle = TapSingleton<unknown> | TapCollection<unknown>;\n\nconst SINGLETON_ID = 'singleton';\n\nconst PROBE_KEYS = new Set([\n 'then',\n 'toJSON',\n '$$typeof',\n 'constructor',\n 'prototype',\n 'valueOf',\n 'toString',\n 'inspect',\n 'asymmetricMatch',\n 'nodeType',\n]);\n\nfunction unknownCollectionError(collection: string): TapError {\n return new TapError(\n 'TAP_UNKNOWN_COLLECTION',\n `This mock exposes no storage collection called \"${collection}\". createTapMock({ storage: … }) 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.`,\n );\n}\n\nfunction singletonView(handle: StorageHandle): TapSingleton<unknown> {\n return {\n get: () => handle.get(),\n set: document => handle.set(document),\n patch: partial => handle.patch(partial),\n };\n}\n\nfunction collectionView(handle: StorageHandle): TapCollection<unknown> {\n return {\n list: listOptions => handle.list(listOptions),\n get: id => handle.get(id),\n put: (id, document) => handle.put(id, document),\n delete: id => handle.delete(id),\n };\n}\n\nfunction scopeError(scope: ScopeKey): TapError {\n return new TapError(\n 'TAP_SCOPE_MISSING',\n `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.`,\n );\n}\n\nfunction asObject(value: unknown): object {\n return typeof value === 'object' && value !== null ? value : {};\n}\n\n/**\n * Builds the bridge a component talks to in a test, without rendering anything.\n *\n * @remarks\n * Data queries answer from the sandbox fixtures, storage is an in-memory store, and\n * everything else the host would do is recorded in `calls`. Scopes default to all of\n * them: pass `scopes` to test the refused path, where a data query rejects with\n * `TAP_SCOPE_MISSING` for the kind's own scope and every storage call, reads\n * included, needs `storage:write`. A procedure needs either `server`, which answers\n * in process, or `handler`, which is called over a real `Request` with a token the\n * mock signs — so that handler has to be built with `jwks: await testJwks()` and the\n * same `extensionId` — and without either, `tap.server.<name>` rejects with\n * `TAP_UNKNOWN_PROCEDURE`. With `storage` given, a collection the map does not name\n * throws `TAP_UNKNOWN_COLLECTION`. Unlike the host, this mock records\n * `tap.actions.run` and resolves it as pending rather than refusing it, so a test\n * can assert on the call today. Use `renderWithTap` when there is a component to\n * render.\n *\n * @example\n * ```ts\n * import { createTapMock } from '@tappify/extension-sdk/testing';\n * import { expect, it } from 'vitest';\n *\n * it('answers a keyword query from the fixtures', async () => {\n * const mock = createTapMock({ scopes: ['store.metadata:read'] });\n * const keywords = await mock.tap.data.query({ kind: 'keywords' });\n * expect(keywords.keywords).toHaveLength(3);\n * });\n * ```\n */\nexport function createTapMock(options: CreateTapMockOptions = {}): TapMock {\n const granted = new Set<ScopeKey>(options.scopes ?? SCOPE_KEYS);\n const listeners = new Map<TapChangeKey, Set<() => void>>();\n const eventHandlers = new Map<string, Set<(payload: unknown) => void>>();\n const state = new Map<string, unknown>();\n const store = new Map<string, StoredDocument[]>();\n\n const portal = document.createElement('div');\n portal.dataset.tapPortal = 'true';\n\n const calls: TapMockCalls = {\n navigations: [],\n searches: [],\n toasts: [],\n confirms: [],\n actions: [],\n telemetry: [],\n chats: [],\n externals: [],\n downloads: [],\n copies: [],\n cards: [],\n expands: 0,\n settingsOpens: 0,\n };\n\n const emitChange = (key: TapChangeKey): void => {\n for (const listener of listeners.get(key) ?? []) listener();\n };\n\n const subscribe = (key: TapChangeKey, listener: () => void): (() => void) => {\n const set = listeners.get(key) ?? new Set<() => void>();\n set.add(listener);\n listeners.set(key, set);\n return () => {\n set.delete(listener);\n };\n };\n\n const withStorage = <T>(run: () => T): Promise<T> =>\n granted.has('storage:write')\n ? Promise.resolve(run())\n : Promise.reject(scopeError('storage:write'));\n\n const bucket = (collection: string): StoredDocument[] => {\n const existing = store.get(collection);\n if (existing) return existing;\n const created: StoredDocument[] = [];\n store.set(collection, created);\n return created;\n };\n\n function buildHandle(collection: string): StorageHandle {\n const find = (id: string): StoredDocument | undefined =>\n bucket(collection).find(entry => entry.id === id);\n\n const write = (id: string, data: unknown): void => {\n const items = bucket(collection);\n const index = items.findIndex(entry => entry.id === id);\n if (index === -1) items.push({ id, data });\n else items[index] = { id, data };\n emitChange(`storage:${collection}`);\n };\n\n return {\n get: (id?: string) =>\n withStorage(() => find(id ?? SINGLETON_ID)?.data ?? null),\n set: document =>\n withStorage(() => {\n write(SINGLETON_ID, document);\n }),\n patch: partial =>\n withStorage(() => {\n const merged = { ...asObject(find(SINGLETON_ID)?.data), ...partial };\n write(SINGLETON_ID, merged);\n return merged;\n }),\n list: () =>\n withStorage(() => ({\n items: bucket(collection)\n .filter(entry => entry.id !== SINGLETON_ID)\n .map(entry => ({ ...asObject(entry.data), id: entry.id })),\n })),\n put: (id, document) =>\n withStorage(() => {\n write(id, document);\n }),\n delete: id =>\n withStorage(() => {\n const items = bucket(collection);\n const index = items.findIndex(entry => entry.id === id);\n if (index !== -1) {\n items.splice(index, 1);\n emitChange(`storage:${collection}`);\n }\n }),\n };\n }\n\n const handles = new Map<string, ExposedHandle>();\n const kinds = options.storage;\n\n const exposeHandle = (collection: string): ExposedHandle => {\n const kind = kinds?.[collection];\n if (kinds !== undefined && kind === undefined) {\n throw unknownCollectionError(collection);\n }\n\n const handle = buildHandle(collection);\n if (kind === 'singleton') return singletonView(handle);\n if (kind === 'collection') return collectionView(handle);\n return handle;\n };\n\n const handleFor = (collection: string): ExposedHandle | undefined => {\n if (PROBE_KEYS.has(collection)) return undefined;\n\n const existing = handles.get(collection);\n if (existing) return existing;\n const created = exposeHandle(collection);\n handles.set(collection, created);\n return created;\n };\n\n const storageTarget: TapStorage = {};\n const storage = new Proxy(storageTarget, {\n get: (_target, property) =>\n typeof property === 'string' ? handleFor(property) : undefined,\n has: () => true,\n ownKeys: () => [...store.keys()],\n getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true }),\n });\n\n let calledProcedures = 0;\n let signedToken: Promise<string> | null = null;\n\n function handlerToken(): Promise<string> {\n if (options.handlerToken !== undefined) {\n return Promise.resolve(options.handlerToken);\n }\n signedToken ??= signTestToken(\n {\n installId: tap.extension.installId,\n extensionId: tap.extension.id,\n projectId: tap.extension.projectId,\n organizationId: tap.auth.organizationId,\n scopes: [...granted],\n },\n { extensionId: tap.extension.id },\n );\n return signedToken;\n }\n\n async function callProcedure(name: string, input: unknown): Promise<unknown> {\n const local = options.server?.[name];\n if (local) return local(input);\n\n if (options.handler) {\n const path = `/tappify/procedures/${name}`;\n calledProcedures += 1;\n const response = await options.handler(\n new Request(`https://vendor.test${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-tappify-event-id': `evt_${name}_${String(calledProcedures)}`,\n authorization: `Bearer ${await handlerToken()}`,\n },\n body: callEnvelope({\n install: {\n id: tap.extension.installId,\n extensionId: tap.extension.id,\n projectId: tap.extension.projectId,\n organizationId: tap.auth.organizationId,\n },\n filters: tap.filters,\n input,\n }),\n }),\n );\n\n return readEnvelopeResponse(response, `POST ${path}`);\n }\n\n throw new TapError(\n 'TAP_UNKNOWN_PROCEDURE',\n `No handler is registered for the procedure \"${name}\". Pass server: { ${name}: input => … } or handler: <your createTappifyHandler handler> to createTapMock().`,\n );\n }\n\n const serverTarget: TapProcedures = {};\n\n const tap: Tap = {\n extension: {\n id: options.extensionId ?? 'starter',\n name: options.extensionName ?? 'Starter',\n installId: options.installId ?? 'ins_test',\n projectId: 'prj_north',\n },\n env: 'dev',\n host: { version: 'test', sdkMajors: [2] },\n auth: {\n token: 'test-token',\n user: { id: 'usr_test', role: 'admin' },\n organizationId: 'org_test',\n scopes: [...granted],\n can: scope => granted.has(scope),\n },\n project: {\n id: 'prj_north',\n name: 'Northlight',\n platforms: ['ios', 'android'],\n apps: [],\n releases: [],\n keywords: [],\n ...options.project,\n },\n filters: { ...DEFAULT_FILTERS, ...options.filters },\n theme: { mode: options.theme ?? 'light' },\n locale: 'en-US',\n timezone: 'UTC',\n format: {\n number: (value, formatOptions) =>\n new Intl.NumberFormat('en-US', formatOptions).format(value),\n currency: (value, code) =>\n new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: code,\n }).format(value),\n date: value => new Date(value).toISOString().slice(0, 10),\n relative: value => new Date(value).toISOString(),\n },\n nav: {\n push: path => calls.navigations.push(path),\n setSearch: params => calls.searches.push(params),\n openSettings: () => {\n calls.settingsOpens += 1;\n },\n openChat: (prompt, context) => calls.chats.push({ prompt, context }),\n openExternal: url => calls.externals.push(url),\n },\n data: {\n query: query => {\n const scope = QUERY_SCOPES[query.kind];\n if (!granted.has(scope)) return Promise.reject(scopeError(scope));\n return Promise.resolve(answerFixtureQuery(query));\n },\n subscribe: <E extends keyof TapHostEvents>(\n event: E,\n handler: (payload: TapHostEvents[E]) => void,\n ): (() => void) => {\n const key = String(event);\n const set = eventHandlers.get(key) ?? new Set<(p: unknown) => void>();\n const wrapped = (payload: unknown): void => {\n handler(payload as TapHostEvents[E]);\n };\n set.add(wrapped);\n eventHandlers.set(key, set);\n return () => {\n set.delete(wrapped);\n };\n },\n },\n server: new Proxy(serverTarget, {\n get:\n (_target, property) =>\n (input: unknown): Promise<unknown> =>\n callProcedure(String(property), input),\n has: () => true,\n }),\n state: {\n get: <T>(key: string): T | undefined => {\n const value = state.get(key);\n return value === undefined ? undefined : (value as T);\n },\n set: (key, value) => {\n state.set(key, value);\n emitChange(`state:${key}`);\n },\n subscribe: (key, handler) => subscribe(`state:${key}`, handler),\n },\n ui: {\n size: options.size ?? 'slot',\n toast: (message, tone = 'neutral') =>\n calls.toasts.push({ message, tone }),\n confirm: confirmOptions => {\n calls.confirms.push(confirmOptions);\n return Promise.resolve(options.confirmAnswer ?? true);\n },\n openInChat: card => calls.cards.push(card),\n expand: () => {\n calls.expands += 1;\n },\n download: (blob, filename) =>\n calls.downloads.push({ filename, size: blob.size }),\n copy: text => {\n calls.copies.push(text);\n return Promise.resolve();\n },\n },\n context: options.context ?? {},\n params: options.params ?? {},\n storage,\n telemetry: {\n event: (name, props) => calls.telemetry.push({ name, props }),\n },\n actions: {\n run: (actionId, input) => {\n calls.actions.push({ actionId, input });\n return Promise.resolve({\n id: `run_${String(calls.actions.length)}`,\n actionId,\n status: 'pending' as const,\n requestedAt: new Date(0).toISOString(),\n });\n },\n },\n invalidate: () => undefined,\n __portal: portal,\n __subscribe: subscribe,\n };\n\n return {\n tap,\n calls,\n portal,\n documents: (collection, documentId) =>\n bucket(collection)\n .filter(entry => documentId === undefined || entry.id === documentId)\n .map(entry => entry.data),\n emit: (event, payload) => {\n for (const handler of eventHandlers.get(String(event)) ?? []) {\n handler(payload);\n }\n },\n setFilters: filters => {\n tap.filters = filters;\n emitChange('filters');\n },\n setSize: size => {\n tap.ui = { ...tap.ui, size };\n emitChange('size');\n },\n setTheme: mode => {\n tap.theme = { mode };\n emitChange('theme');\n },\n setParams: params => {\n tap.params = params;\n emitChange('params');\n },\n };\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunk6HETNZJYcjs = require('./chunk-6HETNZJY.cjs');
3
+ var _chunk2DBSVKAUcjs = require('./chunk-2DBSVKAU.cjs');
4
4
 
5
5
  // src/client/context.ts
6
6
  var _react = require('react');
@@ -18,7 +18,7 @@ var TapContext = resolveContext();
18
18
  function useTap() {
19
19
  const tap = _react.useContext.call(void 0, TapContext);
20
20
  if (tap === null) {
21
- throw new (0, _chunk6HETNZJYcjs.TapError)(
21
+ throw new (0, _chunk2DBSVKAUcjs.TapError)(
22
22
  "TAP_OUTSIDE_HOST",
23
23
  "useTap() ran outside a Tappify mount. Render this component from an entry declared in tappify.extension.json, or wrap it in renderWithTap() from @tappify/extension-sdk/testing."
24
24
  );
@@ -30,4 +30,4 @@ function useTap() {
30
30
 
31
31
 
32
32
  exports.TapContext = TapContext; exports.useTap = useTap;
33
- //# sourceMappingURL=chunk-PRCZMOLD.cjs.map
33
+ //# sourceMappingURL=chunk-OPPYEA7F.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/chunk-PRCZMOLD.cjs","../src/client/context.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA,8BAAwD;AAIxD,IAAM,aAAA,EAAe,qCAAA;AAMrB,IAAM,SAAA,EAAgD,UAAA;AAEtD,SAAS,cAAA,CAAA,EAAsC;AAC7C,EAAA,MAAM,SAAA,EAAW,QAAA,CAAS,YAAY,CAAA;AACtC,EAAA,GAAA,CAAI,QAAA,EAAU,OAAO,QAAA;AAErB,EAAA,MAAM,QAAA,EAAU,kCAAA,IAA8B,CAAA;AAC9C,EAAA,OAAA,CAAQ,YAAA,EAAc,YAAA;AACtB,EAAA,QAAA,CAAS,YAAY,EAAA,EAAI,OAAA;AACzB,EAAA,OAAO,OAAA;AACT;AAEO,IAAM,WAAA,EAAkC,cAAA,CAAe,CAAA;AA0BvD,SAAS,MAAA,CAAA,EAAc;AAC5B,EAAA,MAAM,IAAA,EAAM,+BAAA,UAAqB,CAAA;AACjC,EAAA,GAAA,CAAI,IAAA,IAAQ,IAAA,EAAM;AAChB,IAAA,MAAM,IAAI,+BAAA;AAAA,MACR,kBAAA;AAAA,MACA;AAAA,IACF,CAAA;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AD9BA;AACA;AACE;AACA;AACF,yDAAC","file":"/home/runner/work/extension-sdk/extension-sdk/dist/chunk-PRCZMOLD.cjs","sourcesContent":[null,"import { createContext, useContext, type Context } from 'react';\nimport { TapError } from './errors';\nimport type { Tap } from './types';\n\nconst REGISTRY_KEY = '__tappifyExtensionSdkTapContextV2__';\n\ninterface ContextRegistry {\n [REGISTRY_KEY]?: Context<Tap | null>;\n}\n\nconst registry: typeof globalThis & ContextRegistry = globalThis;\n\nfunction resolveContext(): Context<Tap | null> {\n const existing = registry[REGISTRY_KEY];\n if (existing) return existing;\n\n const created = createContext<Tap | null>(null);\n created.displayName = 'TapContext';\n registry[REGISTRY_KEY] = created;\n return created;\n}\n\nexport const TapContext: Context<Tap | null> = resolveContext();\n\n/**\n * Returns the whole bridge the host mounted this component with.\n *\n * @remarks\n * Throws `TapError` with code `TAP_OUTSIDE_HOST` when no Tappify mount is above\n * the component. The object it returns does not re-render the component when the\n * host changes a member; read through `useTapFilters`, `useTapTheme` and the\n * other slice hooks where that matters.\n *\n * @example\n * ```tsx\n * import { useTap } from '@tappify/extension-sdk';\n * import { Button } from '@tappify/extension-sdk/ui';\n *\n * function CopyId() {\n * const tap = useTap();\n * return (\n * <Button onClick={() => void tap.ui.copy(tap.project.id)}>\n * Copy project id\n * </Button>\n * );\n * }\n * ```\n */\nexport function useTap(): Tap {\n const tap = useContext(TapContext);\n if (tap === null) {\n throw new TapError(\n 'TAP_OUTSIDE_HOST',\n 'useTap() ran outside a Tappify mount. Render this component from an entry declared in tappify.extension.json, or wrap it in renderWithTap() from @tappify/extension-sdk/testing.',\n );\n }\n return tap;\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/chunk-OPPYEA7F.cjs","../src/client/context.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA,8BAAwD;AAIxD,IAAM,aAAA,EAAe,qCAAA;AAMrB,IAAM,SAAA,EAAgD,UAAA;AAEtD,SAAS,cAAA,CAAA,EAAsC;AAC7C,EAAA,MAAM,SAAA,EAAW,QAAA,CAAS,YAAY,CAAA;AACtC,EAAA,GAAA,CAAI,QAAA,EAAU,OAAO,QAAA;AAErB,EAAA,MAAM,QAAA,EAAU,kCAAA,IAA8B,CAAA;AAC9C,EAAA,OAAA,CAAQ,YAAA,EAAc,YAAA;AACtB,EAAA,QAAA,CAAS,YAAY,EAAA,EAAI,OAAA;AACzB,EAAA,OAAO,OAAA;AACT;AAEO,IAAM,WAAA,EAAkC,cAAA,CAAe,CAAA;AAyBvD,SAAS,MAAA,CAAA,EAAc;AAC5B,EAAA,MAAM,IAAA,EAAM,+BAAA,UAAqB,CAAA;AACjC,EAAA,GAAA,CAAI,IAAA,IAAQ,IAAA,EAAM;AAChB,IAAA,MAAM,IAAI,+BAAA;AAAA,MACR,kBAAA;AAAA,MACA;AAAA,IACF,CAAA;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AD7BA;AACA;AACE;AACA;AACF,yDAAC","file":"/home/runner/work/extension-sdk/extension-sdk/dist/chunk-OPPYEA7F.cjs","sourcesContent":[null,"import { createContext, useContext, type Context } from 'react';\nimport { TapError } from './errors';\nimport type { Tap } from './types';\n\nconst REGISTRY_KEY = '__tappifyExtensionSdkTapContextV2__';\n\ninterface ContextRegistry {\n [REGISTRY_KEY]?: Context<Tap | null>;\n}\n\nconst registry: typeof globalThis & ContextRegistry = globalThis;\n\nfunction resolveContext(): Context<Tap | null> {\n const existing = registry[REGISTRY_KEY];\n if (existing) return existing;\n\n const created = createContext<Tap | null>(null);\n created.displayName = 'TapContext';\n registry[REGISTRY_KEY] = created;\n return created;\n}\n\nexport const TapContext: Context<Tap | null> = resolveContext();\n\n/**\n * Returns the whole bridge the host mounted this component with.\n *\n * @remarks\n * Throws `TapError` with code `TAP_OUTSIDE_HOST` when no Tappify mount is above\n * the component. The object it returns does not re-render the component when the\n * host changes a member; read through `useTapFilters`, `useTapTheme` and the\n * other slice hooks where that matters.\n *\n * @example\n * ```tsx\n * import { TapButton, useTap } from '@tappify/extension-sdk';\n *\n * function CopyId() {\n * const tap = useTap();\n * return (\n * <TapButton onClick={() => void tap.ui.copy(tap.project.id)}>\n * Copy project id\n * </TapButton>\n * );\n * }\n * ```\n */\nexport function useTap(): Tap {\n const tap = useContext(TapContext);\n if (tap === null) {\n throw new TapError(\n 'TAP_OUTSIDE_HOST',\n 'useTap() ran outside a Tappify mount. Render this component from an entry declared in tappify.extension.json, or wrap it in renderWithTap() from @tappify/extension-sdk/testing.',\n );\n }\n return tap;\n}\n"]}
@@ -35,13 +35,6 @@ var SCOPES = [
35
35
  requiresSecurityReview: false,
36
36
  requiresJustification: false
37
37
  },
38
- {
39
- key: "review.threads:read",
40
- label: "Read Review threads",
41
- description: "Review threads, comments, screenshot descriptors, build context and safe device context",
42
- requiresSecurityReview: false,
43
- requiresJustification: false
44
- },
45
38
  {
46
39
  key: "crashes:read",
47
40
  label: "Read crash data",
@@ -115,7 +108,7 @@ var SCOPES = [
115
108
  {
116
109
  key: "work:sync",
117
110
  label: "Sync work items",
118
- description: "Sync comments and status on created work items",
111
+ description: "Two-way comments and status on created work items",
119
112
  requiresSecurityReview: false,
120
113
  requiresJustification: true
121
114
  },
@@ -306,4 +299,4 @@ var slotsForPage = (page) => _nullishCoalesce(_optionalChain([findPage, 'call',
306
299
 
307
300
 
308
301
  exports.SCOPES = SCOPES; exports.SCOPE_KEYS = SCOPE_KEYS; exports.SLOTS = SLOTS; exports.CHARTS = CHARTS; exports.PAGES = PAGES; exports.PAGE_KEYS = PAGE_KEYS; exports.TABLES = TABLES; exports.TABLE_KEYS = TABLE_KEYS; exports.KINDS = KINDS; exports.CATEGORIES = CATEGORIES; exports.EVENTS = EVENTS; exports.SERVED_SDK_MAJORS = SERVED_SDK_MAJORS; exports.QUERY_SCOPES = QUERY_SCOPES; exports.CARD_KEYS = CARD_KEYS; exports.findScope = findScope; exports.findPage = findPage; exports.findTable = findTable; exports.slotsForPage = slotsForPage;
309
- //# sourceMappingURL=chunk-7YKH4LZN.cjs.map
302
+ //# sourceMappingURL=chunk-PYJMJF44.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/extension-sdk/extension-sdk/dist/chunk-PYJMJF44.cjs","../src/manifest/constants.ts"],"names":[],"mappings":"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","file":"/home/runner/work/extension-sdk/extension-sdk/dist/chunk-PYJMJF44.cjs","sourcesContent":[null,"import type { ToolDeclaration } from './types';\n\nexport const SCOPES = [\n {\n key: 'ui:render',\n label: 'Render UI',\n description:\n 'Render the declared pages, tabs, widgets, row actions and settings panel, and open chat with a visible prompt',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'projects:read',\n label: 'Read the project',\n description: 'Project name, platforms and store identity',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'analytics:read',\n label: 'Read analytics',\n description: 'Downloads, impressions, page views and conversion series',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'store.metadata:read',\n label: 'Read store metadata',\n description: 'Listing metadata, keyword set and keyword positions',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'reviews:read',\n label: 'Read store reviews',\n description: 'Review text, ratings and replies',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'crashes:read',\n label: 'Read crash data',\n description: 'Crash-free rate and issue counts',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'revenue:read',\n label: 'Read revenue',\n description: 'MRR, revenue series and refund counts',\n requiresSecurityReview: false,\n requiresJustification: true,\n },\n {\n key: 'metrics:write',\n label: 'Write connector metrics',\n description:\n 'Write the metrics this extension declares into the metrics store',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'alerts:write',\n label: 'Raise alerts and banners',\n description:\n 'Turn declared webhooks into alerts and banners, at most three a day per project',\n requiresSecurityReview: false,\n requiresJustification: true,\n },\n {\n key: 'insights:write',\n label: 'Post insights',\n description: 'Post insights to Home and to chat',\n requiresSecurityReview: false,\n requiresJustification: true,\n },\n {\n key: 'ai:tools',\n label: 'Add assistant tools',\n description: 'Offer tools and mentions to the assistant',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'ai:actions',\n label: 'Run assistant actions',\n description: 'Run declared actions, each behind the approval card',\n requiresSecurityReview: true,\n requiresJustification: true,\n },\n {\n key: 'store.metadata:write',\n label: 'Propose store metadata changes',\n description: 'Propose listing changes through a Tappify approval',\n requiresSecurityReview: true,\n requiresJustification: true,\n },\n {\n key: 'autopilot:trigger',\n label: 'Trigger automated store actions',\n description: 'Start automated store actions on the owner’s behalf',\n requiresSecurityReview: true,\n requiresJustification: true,\n },\n {\n key: 'work:create',\n label: 'Create work items',\n description: 'Create issues, pages or messages in the vendor’s system',\n requiresSecurityReview: false,\n requiresJustification: true,\n },\n {\n key: 'work:sync',\n label: 'Sync work items',\n description: 'Two-way comments and status on created work items',\n requiresSecurityReview: false,\n requiresJustification: true,\n },\n {\n key: 'storage:write',\n label: 'Use hosted storage',\n description:\n 'Read and write this extension’s own hosted storage collections',\n requiresSecurityReview: false,\n requiresJustification: false,\n },\n {\n key: 'messaging:send',\n label: 'Send messages to end users',\n description:\n 'Send messages or push notifications to the owner’s end users through owner-supplied credentials',\n requiresSecurityReview: true,\n requiresJustification: true,\n },\n {\n key: 'ai:skills',\n label: 'Shape the assistant',\n description:\n 'Add skills, prompt templates and context providers that shape the assistant’s behaviour',\n requiresSecurityReview: false,\n requiresJustification: true,\n },\n] as const;\n\nexport type ScopeDefinition = (typeof SCOPES)[number];\n/** Every scope an extension can ask for under `scopes` in its manifest. */\nexport type ScopeKey = ScopeDefinition['key'];\n\nexport const SCOPE_KEYS: readonly ScopeKey[] = SCOPES.map(scope => scope.key);\n\nexport const SLOTS = [\n 'kpi-row',\n 'insights',\n 'sidebar',\n 'below-chart',\n 'below-list',\n] as const;\n/** The anchors on a host page a widget can be declared at. */\nexport type SlotId = (typeof SLOTS)[number];\n\nexport const CHARTS = [\n 'installs-activity',\n 'conversion',\n 'release-cadence',\n] as const;\nexport type HostChart = (typeof CHARTS)[number];\n\nexport const PAGES = [\n {\n key: 'overview',\n label: 'Home',\n route: '/projects/:id/overview',\n slots: ['kpi-row', 'insights', 'sidebar'],\n charts: ['installs-activity'],\n tabs: true,\n banners: true,\n },\n {\n key: 'analytics',\n label: 'Analytics',\n route: '/projects/:id/analytics',\n slots: ['kpi-row', 'below-chart', 'sidebar'],\n charts: ['installs-activity', 'conversion'],\n tabs: true,\n banners: true,\n },\n {\n key: 'deployments',\n label: 'Deployments',\n route: '/projects/:id/deployments',\n slots: ['sidebar', 'below-list'],\n charts: ['release-cadence'],\n tabs: true,\n banners: false,\n },\n {\n key: 'ai_chat',\n label: 'Assistant',\n route: 'chat',\n slots: [],\n charts: [],\n tabs: false,\n banners: false,\n },\n] as const;\n\nexport type PageDefinition = (typeof PAGES)[number];\n/** The host pages a contribution can attach a tab, widget or banner to. */\nexport type HostPage = PageDefinition['key'];\n\nexport const PAGE_KEYS: readonly HostPage[] = PAGES.map(page => page.key);\n\nexport const TABLES = [\n { key: 'analytics.keywords', label: 'Keywords' },\n { key: 'analytics.reviews', label: 'Reviews' },\n { key: 'deployments.releases', label: 'Releases' },\n { key: 'deployments.builds', label: 'Builds' },\n] as const;\n\nexport type TableDefinition = (typeof TABLES)[number];\nexport type HostTable = TableDefinition['key'];\n\nexport const TABLE_KEYS: readonly HostTable[] = TABLES.map(table => table.key);\n\nexport const KINDS = [\n 'page',\n 'tab',\n 'widget',\n 'series',\n 'banner',\n 'rowAction',\n 'marker',\n 'settings',\n 'storage',\n 'connector',\n 'webhook',\n 'tool',\n 'mention',\n 'action',\n 'knowledge',\n 'skill',\n 'prompt',\n 'context',\n 'work',\n] as const;\nexport type ContributionKind = (typeof KINDS)[number];\n\nexport const CATEGORIES = [\n 'market_data',\n 'subscriptions',\n 'product_analytics',\n 'crashes',\n 'attribution',\n 'experiments',\n 'messaging',\n 'session_replay',\n 'localization',\n 'design',\n 'ci_cd',\n 'trackers',\n 'support',\n 'chat',\n 'ad_monetization',\n 'warehouse',\n 'compliance',\n 'additional_stores',\n] as const;\nexport type ExtensionCategory = (typeof CATEGORIES)[number];\n\nexport const EVENTS = [\n 'release.shipped',\n 'metadata.changed',\n 'keyword.set_changed',\n 'screenshots.updated',\n 'price.changed',\n 'featuring.started',\n 'score.changed',\n 'incident.opened',\n 'incident.resolved',\n 'playbook.step_approved',\n 'review.thread_opened',\n 'review.thread_resolved',\n 'approval.requested',\n 'digest.sent',\n 'install.created',\n 'install.paused',\n 'install.resumed',\n 'install.revoked',\n 'install.token_rotated',\n 'scopes.changed',\n 'settings.changed',\n] as const;\n/** Every event Tappify emits, which a mount or a server can subscribe to. */\nexport type HostEventName = (typeof EVENTS)[number];\n\nexport const SERVED_SDK_MAJORS: readonly number[] = [2];\n\n/** The `kind` of every read `tap.data.query` accepts, one per scope it needs. */\nexport type DataQueryKind =\n | 'project'\n | 'series'\n | 'keywords'\n | 'listing'\n | 'reviews'\n | 'crashes'\n | 'revenue';\n\nexport const QUERY_SCOPES: Record<DataQueryKind, ScopeKey> = {\n project: 'projects:read',\n series: 'analytics:read',\n keywords: 'store.metadata:read',\n listing: 'store.metadata:read',\n reviews: 'reviews:read',\n crashes: 'crashes:read',\n revenue: 'revenue:read',\n};\n\n/**\n * The keys a tool's answer carries for each `returns` kind it may declare.\n * `validateToolResponse`, `tappify extension doctor` and the host all read\n * this one table, so a wrong body fails the same way wherever it is checked.\n */\nexport const CARD_KEYS: Record<ToolDeclaration['returns'], string[]> = {\n value: ['label', 'value'],\n series: ['label', 'points'],\n table: ['columns', 'rows'],\n list: ['items'],\n comparison: ['label', 'left', 'right'],\n};\n\nexport const findScope = (key: string): ScopeDefinition | undefined =>\n SCOPES.find(scope => scope.key === key);\n\nexport const findPage = (key: string): PageDefinition | undefined =>\n PAGES.find(page => page.key === key);\n\nexport const findTable = (key: string): TableDefinition | undefined =>\n TABLES.find(table => table.key === key);\n\nexport const slotsForPage = (page: string): readonly SlotId[] =>\n findPage(page)?.slots ?? [];\n"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TapError
3
- } from "./chunk-QXD7WVKK.mjs";
3
+ } from "./chunk-CSTYBKZC.mjs";
4
4
 
5
5
  // src/client/context.ts
6
6
  import { createContext, useContext } from "react";
@@ -30,4 +30,4 @@ export {
30
30
  TapContext,
31
31
  useTap
32
32
  };
33
- //# sourceMappingURL=chunk-QA5AL3VZ.mjs.map
33
+ //# sourceMappingURL=chunk-QHM7XNRK.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/context.ts"],"sourcesContent":["import { createContext, useContext, type Context } from 'react';\nimport { TapError } from './errors';\nimport type { Tap } from './types';\n\nconst REGISTRY_KEY = '__tappifyExtensionSdkTapContextV2__';\n\ninterface ContextRegistry {\n [REGISTRY_KEY]?: Context<Tap | null>;\n}\n\nconst registry: typeof globalThis & ContextRegistry = globalThis;\n\nfunction resolveContext(): Context<Tap | null> {\n const existing = registry[REGISTRY_KEY];\n if (existing) return existing;\n\n const created = createContext<Tap | null>(null);\n created.displayName = 'TapContext';\n registry[REGISTRY_KEY] = created;\n return created;\n}\n\nexport const TapContext: Context<Tap | null> = resolveContext();\n\n/**\n * Returns the whole bridge the host mounted this component with.\n *\n * @remarks\n * Throws `TapError` with code `TAP_OUTSIDE_HOST` when no Tappify mount is above\n * the component. The object it returns does not re-render the component when the\n * host changes a member; read through `useTapFilters`, `useTapTheme` and the\n * other slice hooks where that matters.\n *\n * @example\n * ```tsx\n * import { useTap } from '@tappify/extension-sdk';\n * import { Button } from '@tappify/extension-sdk/ui';\n *\n * function CopyId() {\n * const tap = useTap();\n * return (\n * <Button onClick={() => void tap.ui.copy(tap.project.id)}>\n * Copy project id\n * </Button>\n * );\n * }\n * ```\n */\nexport function useTap(): Tap {\n const tap = useContext(TapContext);\n if (tap === null) {\n throw new TapError(\n 'TAP_OUTSIDE_HOST',\n 'useTap() ran outside a Tappify mount. Render this component from an entry declared in tappify.extension.json, or wrap it in renderWithTap() from @tappify/extension-sdk/testing.',\n );\n }\n return tap;\n}\n"],"mappings":";;;;;AAAA,SAAS,eAAe,kBAAgC;AAIxD,IAAM,eAAe;AAMrB,IAAM,WAAgD;AAEtD,SAAS,iBAAsC;AAC7C,QAAM,WAAW,SAAS,YAAY;AACtC,MAAI,SAAU,QAAO;AAErB,QAAM,UAAU,cAA0B,IAAI;AAC9C,UAAQ,cAAc;AACtB,WAAS,YAAY,IAAI;AACzB,SAAO;AACT;AAEO,IAAM,aAAkC,eAAe;AA0BvD,SAAS,SAAc;AAC5B,QAAM,MAAM,WAAW,UAAU;AACjC,MAAI,QAAQ,MAAM;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/client/context.ts"],"sourcesContent":["import { createContext, useContext, type Context } from 'react';\nimport { TapError } from './errors';\nimport type { Tap } from './types';\n\nconst REGISTRY_KEY = '__tappifyExtensionSdkTapContextV2__';\n\ninterface ContextRegistry {\n [REGISTRY_KEY]?: Context<Tap | null>;\n}\n\nconst registry: typeof globalThis & ContextRegistry = globalThis;\n\nfunction resolveContext(): Context<Tap | null> {\n const existing = registry[REGISTRY_KEY];\n if (existing) return existing;\n\n const created = createContext<Tap | null>(null);\n created.displayName = 'TapContext';\n registry[REGISTRY_KEY] = created;\n return created;\n}\n\nexport const TapContext: Context<Tap | null> = resolveContext();\n\n/**\n * Returns the whole bridge the host mounted this component with.\n *\n * @remarks\n * Throws `TapError` with code `TAP_OUTSIDE_HOST` when no Tappify mount is above\n * the component. The object it returns does not re-render the component when the\n * host changes a member; read through `useTapFilters`, `useTapTheme` and the\n * other slice hooks where that matters.\n *\n * @example\n * ```tsx\n * import { TapButton, useTap } from '@tappify/extension-sdk';\n *\n * function CopyId() {\n * const tap = useTap();\n * return (\n * <TapButton onClick={() => void tap.ui.copy(tap.project.id)}>\n * Copy project id\n * </TapButton>\n * );\n * }\n * ```\n */\nexport function useTap(): Tap {\n const tap = useContext(TapContext);\n if (tap === null) {\n throw new TapError(\n 'TAP_OUTSIDE_HOST',\n 'useTap() ran outside a Tappify mount. Render this component from an entry declared in tappify.extension.json, or wrap it in renderWithTap() from @tappify/extension-sdk/testing.',\n );\n }\n return tap;\n}\n"],"mappings":";;;;;AAAA,SAAS,eAAe,kBAAgC;AAIxD,IAAM,eAAe;AAMrB,IAAM,WAAgD;AAEtD,SAAS,iBAAsC;AAC7C,QAAM,WAAW,SAAS,YAAY;AACtC,MAAI,SAAU,QAAO;AAErB,QAAM,UAAU,cAA0B,IAAI;AAC9C,UAAQ,cAAc;AACtB,WAAS,YAAY,IAAI;AACzB,SAAO;AACT;AAEO,IAAM,aAAkC,eAAe;AAyBvD,SAAS,SAAc;AAC5B,QAAM,MAAM,WAAW,UAAU;AACjC,MAAI,QAAQ,MAAM;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/codegen.ts","../src/codegen/generate.ts","../src/client/errors.ts"],"sourcesContent":["export { GENERATED_HEADER, generateTypes } from './codegen/generate';\nexport type {\n GenerateTypesOptions,\n RegistrySnapshot,\n} from './codegen/generate';\n","import { compile } from 'json-schema-to-typescript';\nimport { dirname, join } from 'node:path';\nimport { TapError } from '../client/errors';\nimport type {\n CredentialField,\n ExtensionManifest,\n JsonSchema,\n} from '../manifest/types';\n\nexport const GENERATED_HEADER = `/* eslint-disable */\n/**\n * Generated by \\`tappify extension types\\`. Do not edit by hand.\n * Re-run the command after changing tappify.extension.json or any schema it points at.\n */`;\n\nexport interface RegistrySnapshot {\n events: { name: string; payload?: JsonSchema }[];\n}\n\nexport interface GenerateTypesOptions {\n manifest: ExtensionManifest;\n registrySnapshot?: RegistrySnapshot;\n readSchemaFile?(path: string): JsonSchema;\n /**\n * The extension root. A `$ref` inside a schema file resolves against that\n * file's own directory under it, the way the host and the Tap Store read\n * it; without a root the process directory stands in.\n */\n cwd?: string;\n}\n\nconst STYLE = {\n bracketSpacing: true,\n printWidth: 80,\n semi: true,\n singleQuote: true,\n tabWidth: 2,\n trailingComma: 'all',\n useTabs: false,\n} as const;\n\nfunction pascal(value: string): string {\n return value\n .split(/[^A-Za-z0-9]+/)\n .filter(part => part !== '')\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\ninterface Emitter {\n declare(preferredName: string, schema: JsonSchema): Promise<string>;\n declarations(): string[];\n}\n\ninterface ResolvedSchema {\n schema: JsonSchema;\n /** Where the schema's own nested `$ref`s resolve from. */\n cwd?: string;\n}\n\nfunction createEmitter(\n resolveSchema: (schema: JsonSchema) => ResolvedSchema,\n): Emitter {\n const used = new Set<string>();\n const blocks: string[] = [];\n\n return {\n async declare(preferredName, schema) {\n let name = pascal(preferredName);\n let suffix = 2;\n while (used.has(name)) {\n name = `${pascal(preferredName)}${String(suffix)}`;\n suffix += 1;\n }\n used.add(name);\n\n const resolved = resolveSchema(schema);\n const input: Parameters<typeof compile>[0] = {\n ...resolved.schema,\n title: name,\n };\n\n const text = await compile(input, name, {\n bannerComment: '',\n additionalProperties: false,\n declareExternallyReferenced: true,\n enableConstEnums: false,\n format: true,\n style: STYLE,\n unknownAny: true,\n ...(resolved.cwd === undefined ? {} : { cwd: resolved.cwd }),\n });\n\n blocks.push(text.trim());\n return name;\n },\n declarations() {\n return blocks;\n },\n };\n}\n\nfunction credentialSchema(fields: CredentialField[]): JsonSchema {\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n\n for (const field of fields) {\n const property: JsonSchema =\n field.type === 'select' && field.options\n ? { type: 'string', enum: field.options }\n : { type: 'string' };\n if (field.help !== undefined) property.description = field.help;\n properties[field.name] = property;\n if (field.required) required.push(field.name);\n }\n\n return { type: 'object', properties, required, additionalProperties: false };\n}\n\nexport async function generateTypes(\n options: GenerateTypesOptions,\n): Promise<string> {\n const { manifest, registrySnapshot } = options;\n const contributes = manifest.contributes;\n\n const resolveSchema = (schema: JsonSchema): ResolvedSchema => {\n const ref = schema.$ref;\n if (typeof ref !== 'string' || ref.startsWith('#')) {\n return { schema, cwd: options.cwd };\n }\n if (options.readSchemaFile === undefined) {\n throw new TapError(\n 'TAP_SCHEMA_UNRESOLVED',\n `The manifest points at the schema file \"${ref}\", but this call passed no readSchemaFile. Run \\`tappify extension types\\`, which reads schema files from the extension directory, or supply readSchemaFile.`,\n );\n }\n return {\n schema: options.readSchemaFile(ref),\n cwd:\n options.cwd === undefined ? undefined : dirname(join(options.cwd, ref)),\n };\n };\n\n const emitter = createEmitter(resolveSchema);\n const augmentations: string[] = [];\n\n const storageEntries = Object.entries(contributes.storage ?? {});\n if (storageEntries.length > 0) {\n const lines: string[] = [];\n for (const [name, collection] of storageEntries) {\n const typeName = await emitter.declare(name, collection.schema);\n const kind = collection.singleton === true ? 'singleton' : 'collection';\n lines.push(\n ` '${name}': { kind: '${kind}'; scope: '${collection.scope}'; document: ${typeName} };`,\n );\n }\n augmentations.push(` interface TapStorageMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n const procedureEntries = Object.entries(manifest.server?.procedures ?? {});\n if (procedureEntries.length > 0) {\n const lines: string[] = [];\n for (const [name, declaration] of procedureEntries) {\n const inputName = await emitter.declare(\n `${name}Input`,\n declaration.input,\n );\n const outputName = await emitter.declare(\n `${name}Output`,\n declaration.output,\n );\n lines.push(\n ` '${name}': { input: ${inputName}; output: ${outputName} };`,\n );\n }\n augmentations.push(\n ` interface TapProcedureMap {\\n${lines.join('\\n')}\\n }`,\n );\n }\n\n const actions = contributes.ai?.actions ?? [];\n if (actions.length > 0) {\n const lines: string[] = [];\n for (const action of actions) {\n const name = await emitter.declare(`${action.id}Input`, action.input);\n lines.push(` '${action.id}': { input: ${name} };`);\n }\n augmentations.push(` interface TapActionMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n const tools = contributes.ai?.tools ?? [];\n if (tools.length > 0) {\n const lines: string[] = [];\n for (const tool of tools) {\n const name = await emitter.declare(`${tool.id}Input`, tool.input);\n lines.push(` '${tool.id}': { input: ${name} };`);\n }\n augmentations.push(` interface TapToolMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n const prompts = (contributes.ai?.prompts ?? []).filter(\n prompt => prompt.input !== undefined,\n );\n if (prompts.length > 0) {\n const lines: string[] = [];\n for (const prompt of prompts) {\n const name = await emitter.declare(\n `${prompt.id}PromptInput`,\n prompt.input ?? { type: 'object' },\n );\n lines.push(` '${prompt.id}': { input: ${name} };`);\n }\n augmentations.push(` interface TapPromptMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n const webhooks = contributes.webhooks ?? [];\n if (webhooks.length > 0) {\n const lines: string[] = [];\n for (const webhook of webhooks) {\n const name = await emitter.declare(\n `${webhook.event}Payload`,\n webhook.payload,\n );\n lines.push(` '${webhook.event}': ${name};`);\n }\n augmentations.push(` interface TapWebhookMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n if (registrySnapshot !== undefined && registrySnapshot.events.length > 0) {\n const lines: string[] = [];\n for (const event of registrySnapshot.events) {\n const name = await emitter.declare(\n `${event.name}Event`,\n event.payload ?? { type: 'object' },\n );\n lines.push(` '${event.name}': ${name};`);\n }\n augmentations.push(\n ` interface TapHostEventMap {\\n${lines.join('\\n')}\\n }`,\n );\n }\n\n const telemetry = manifest.telemetry ?? [];\n if (telemetry.length > 0) {\n const lines = telemetry.map(name => ` '${name}': true;`);\n augmentations.push(\n ` interface TapTelemetryEvents {\\n${lines.join('\\n')}\\n }`,\n );\n }\n\n if (contributes.settings) {\n const name = await emitter.declare('Settings', contributes.settings.schema);\n augmentations.push(` interface TapSettings extends ${name} {}`);\n }\n\n const credentialFields = contributes.connector?.auth?.fields ?? [];\n if (credentialFields.length > 0) {\n const name = await emitter.declare(\n 'Credentials',\n credentialSchema(credentialFields),\n );\n augmentations.push(` interface TapCredentials extends ${name} {}`);\n }\n\n const declarations = emitter.declarations();\n const module =\n augmentations.length === 0\n ? \"declare module '@tappify/extension-sdk' {}\"\n : `declare module '@tappify/extension-sdk' {\\n${augmentations.join('\\n\\n')}\\n}`;\n\n return [\n GENERATED_HEADER,\n '',\n ...(declarations.length > 0 ? [declarations.join('\\n\\n'), ''] : []),\n module,\n '',\n 'export {};',\n '',\n ].join('\\n');\n}\n","/** Every code a `TapError` the SDK raises carries. */\nexport type TapErrorCode =\n | 'TAP_OUTSIDE_HOST'\n | 'TAP_SCOPE_MISSING'\n | 'TAP_UNKNOWN_COLLECTION'\n | 'TAP_UNKNOWN_PROCEDURE'\n | 'TAP_NO_PORTAL'\n | 'TAP_NO_QUERY_CLIENT'\n | 'TAP_MANIFEST_INVALID'\n | 'TAP_ENTRY_MISSING'\n | 'TAP_STYLES_UNRESOLVED'\n | 'TAP_SCHEMA_UNRESOLVED'\n | 'TAP_TOKEN_MISSING'\n | 'TAP_TOKEN_INVALID'\n | 'TAP_TOKEN_AUDIENCE'\n | 'TAP_EVENT_ID_MISSING'\n | 'TAP_BODY_INVALID'\n | 'TAP_BODY_TOO_LARGE'\n | 'TAP_ROUTE_UNKNOWN'\n | 'TAP_HANDLER_MISSING'\n | 'TAP_INTERNAL_ERROR'\n | 'TAP_WEBHOOK_REJECTED'\n | 'TAP_TEST_TARGET_INVALID';\n\n/**\n * The same codes as `TapErrorCode`, as an array to iterate or check membership\n * against at runtime.\n */\nexport const TAP_ERROR_CODES: readonly TapErrorCode[] = [\n 'TAP_OUTSIDE_HOST',\n 'TAP_SCOPE_MISSING',\n 'TAP_UNKNOWN_COLLECTION',\n 'TAP_UNKNOWN_PROCEDURE',\n 'TAP_NO_PORTAL',\n 'TAP_NO_QUERY_CLIENT',\n 'TAP_MANIFEST_INVALID',\n 'TAP_ENTRY_MISSING',\n 'TAP_STYLES_UNRESOLVED',\n 'TAP_SCHEMA_UNRESOLVED',\n 'TAP_TOKEN_MISSING',\n 'TAP_TOKEN_INVALID',\n 'TAP_TOKEN_AUDIENCE',\n 'TAP_EVENT_ID_MISSING',\n 'TAP_BODY_INVALID',\n 'TAP_BODY_TOO_LARGE',\n 'TAP_ROUTE_UNKNOWN',\n 'TAP_HANDLER_MISSING',\n 'TAP_INTERNAL_ERROR',\n 'TAP_WEBHOOK_REJECTED',\n 'TAP_TEST_TARGET_INVALID',\n];\n\nconst CODE_SET = new Set<string>(TAP_ERROR_CODES);\n\n/**\n * The error the SDK and the host raise when a call cannot be served at all.\n *\n * @remarks\n * The SDK throws it for a mount outside the host (`TAP_OUTSIDE_HOST`), a\n * collection or procedure the manifest does not declare\n * (`TAP_UNKNOWN_COLLECTION`, `TAP_UNKNOWN_PROCEDURE`), a missing portal or query\n * client, a token the server rejected, and a misused testing helper. The host\n * turns a refused read or write into `TAP_SCOPE_MISSING`. Branch on `code`, which\n * is stable; the message is prose and changes. An error your own server raised is\n * a `TapServerError` instead.\n *\n * @example\n * ```tsx\n * import { TapError, useTapQuery } from '@tappify/extension-sdk';\n * import { ErrorState } from '@tappify/extension-sdk/ui';\n *\n * function Revenue() {\n * const revenue = useTapQuery({\n * kind: 'revenue',\n * range: { from: '2026-09-01', to: '2026-09-08' },\n * });\n * if (TapError.is(revenue.error) && revenue.error.code === 'TAP_SCOPE_MISSING') {\n * return <ErrorState title=\"Revenue is not shared with this extension\" />;\n * }\n * return <span>{revenue.data?.mrr ?? 0}</span>;\n * }\n * ```\n */\nexport class TapError extends Error {\n readonly code: TapErrorCode;\n\n constructor(code: TapErrorCode, message: string) {\n super(message);\n this.name = 'TapError';\n this.code = code;\n }\n\n /**\n * Narrows an unknown value to a `TapError`, by name and by a code it\n * recognises, so it holds across module instances.\n */\n static is(value: unknown): value is TapError {\n return (\n value instanceof Error &&\n value.name === 'TapError' &&\n 'code' in value &&\n typeof value.code === 'string' &&\n CODE_SET.has(value.code)\n );\n }\n}\n\n/**\n * The error a vendor server throws to answer a Tappify call with a code and a\n * message the owner sees.\n *\n * @remarks\n * `createTappifyHandler` catches it and answers `{ error: { code, message } }`\n * with `status`, or 400 when no status was given; the host relays both to the\n * calling mount, where they arrive as the `error` of `useTapServer`. Pick a code\n * of your own and keep it stable — the SDK's own codes are `SERVER_ERROR_CODES` —\n * and write the message for the owner, who reads it on screen.\n *\n * @example\n * ```ts\n * import { TapServerError } from '@tappify/extension-sdk/server';\n *\n * function assertRange(days: number) {\n * if (days > 0) return;\n * throw new TapServerError(\n * 'RANGE_TOO_SHORT',\n * 'getSummary needs a range of at least one day. Widen the date picker, then retry.',\n * 400,\n * );\n * }\n * ```\n */\nexport class TapServerError extends Error {\n readonly code: string;\n readonly status: number | undefined;\n\n constructor(code: string, message: string, status?: number) {\n super(message);\n this.name = 'TapServerError';\n this.code = code;\n this.status = status;\n }\n\n /** The wire body the handler answers with, without the status. */\n toJSON(): { error: { code: string; message: string } } {\n return { error: { code: this.code, message: this.message } };\n }\n\n /**\n * Narrows an unknown value to a `TapServerError` by name and by carrying a\n * string `code`, so it holds across module instances.\n */\n static is(value: unknown): value is TapServerError {\n return (\n value instanceof Error &&\n value.name === 'TapServerError' &&\n 'code' in value &&\n typeof value.code === 'string'\n );\n }\n}\n"],"mappings":"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","names":["module"]}
1
+ {"version":3,"sources":["../src/codegen.ts","../src/codegen/generate.ts","../src/client/errors.ts"],"sourcesContent":["export { GENERATED_HEADER, generateTypes } from './codegen/generate';\nexport type {\n GenerateTypesOptions,\n RegistrySnapshot,\n} from './codegen/generate';\n","import { compile } from 'json-schema-to-typescript';\nimport { dirname, join } from 'node:path';\nimport { TapError } from '../client/errors';\nimport type {\n CredentialField,\n ExtensionManifest,\n JsonSchema,\n} from '../manifest/types';\n\nexport const GENERATED_HEADER = `/* eslint-disable */\n/**\n * Generated by \\`tappify extension types\\`. Do not edit by hand.\n * Re-run the command after changing tappify.extension.json or any schema it points at.\n */`;\n\nexport interface RegistrySnapshot {\n events: { name: string; payload?: JsonSchema }[];\n}\n\nexport interface GenerateTypesOptions {\n manifest: ExtensionManifest;\n registrySnapshot?: RegistrySnapshot;\n readSchemaFile?(path: string): JsonSchema;\n /**\n * The extension root. A `$ref` inside a schema file resolves against that\n * file's own directory under it, the way the host and the Tap Store read\n * it; without a root the process directory stands in.\n */\n cwd?: string;\n}\n\nconst STYLE = {\n bracketSpacing: true,\n printWidth: 80,\n semi: true,\n singleQuote: true,\n tabWidth: 2,\n trailingComma: 'all',\n useTabs: false,\n} as const;\n\nfunction pascal(value: string): string {\n return value\n .split(/[^A-Za-z0-9]+/)\n .filter(part => part !== '')\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\ninterface Emitter {\n declare(preferredName: string, schema: JsonSchema): Promise<string>;\n declarations(): string[];\n}\n\ninterface ResolvedSchema {\n schema: JsonSchema;\n /** Where the schema's own nested `$ref`s resolve from. */\n cwd?: string;\n}\n\nfunction createEmitter(\n resolveSchema: (schema: JsonSchema) => ResolvedSchema,\n): Emitter {\n const used = new Set<string>();\n const blocks: string[] = [];\n\n return {\n async declare(preferredName, schema) {\n let name = pascal(preferredName);\n let suffix = 2;\n while (used.has(name)) {\n name = `${pascal(preferredName)}${String(suffix)}`;\n suffix += 1;\n }\n used.add(name);\n\n const resolved = resolveSchema(schema);\n const input: Parameters<typeof compile>[0] = {\n ...resolved.schema,\n title: name,\n };\n\n const text = await compile(input, name, {\n bannerComment: '',\n additionalProperties: false,\n declareExternallyReferenced: true,\n enableConstEnums: false,\n format: true,\n style: STYLE,\n unknownAny: true,\n ...(resolved.cwd === undefined ? {} : { cwd: resolved.cwd }),\n });\n\n blocks.push(text.trim());\n return name;\n },\n declarations() {\n return blocks;\n },\n };\n}\n\nfunction credentialSchema(fields: CredentialField[]): JsonSchema {\n const properties: Record<string, JsonSchema> = {};\n const required: string[] = [];\n\n for (const field of fields) {\n const property: JsonSchema =\n field.type === 'select' && field.options\n ? { type: 'string', enum: field.options }\n : { type: 'string' };\n if (field.help !== undefined) property.description = field.help;\n properties[field.name] = property;\n if (field.required) required.push(field.name);\n }\n\n return { type: 'object', properties, required, additionalProperties: false };\n}\n\nexport async function generateTypes(\n options: GenerateTypesOptions,\n): Promise<string> {\n const { manifest, registrySnapshot } = options;\n const contributes = manifest.contributes;\n\n const resolveSchema = (schema: JsonSchema): ResolvedSchema => {\n const ref = schema.$ref;\n if (typeof ref !== 'string' || ref.startsWith('#')) {\n return { schema, cwd: options.cwd };\n }\n if (options.readSchemaFile === undefined) {\n throw new TapError(\n 'TAP_SCHEMA_UNRESOLVED',\n `The manifest points at the schema file \"${ref}\", but this call passed no readSchemaFile. Run \\`tappify extension types\\`, which reads schema files from the extension directory, or supply readSchemaFile.`,\n );\n }\n return {\n schema: options.readSchemaFile(ref),\n cwd:\n options.cwd === undefined ? undefined : dirname(join(options.cwd, ref)),\n };\n };\n\n const emitter = createEmitter(resolveSchema);\n const augmentations: string[] = [];\n\n const storageEntries = Object.entries(contributes.storage ?? {});\n if (storageEntries.length > 0) {\n const lines: string[] = [];\n for (const [name, collection] of storageEntries) {\n const typeName = await emitter.declare(name, collection.schema);\n const kind = collection.singleton === true ? 'singleton' : 'collection';\n lines.push(\n ` '${name}': { kind: '${kind}'; scope: '${collection.scope}'; document: ${typeName} };`,\n );\n }\n augmentations.push(` interface TapStorageMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n const procedureEntries = Object.entries(manifest.server?.procedures ?? {});\n if (procedureEntries.length > 0) {\n const lines: string[] = [];\n for (const [name, declaration] of procedureEntries) {\n const inputName = await emitter.declare(\n `${name}Input`,\n declaration.input,\n );\n const outputName = await emitter.declare(\n `${name}Output`,\n declaration.output,\n );\n lines.push(\n ` '${name}': { input: ${inputName}; output: ${outputName} };`,\n );\n }\n augmentations.push(\n ` interface TapProcedureMap {\\n${lines.join('\\n')}\\n }`,\n );\n }\n\n const actions = contributes.ai?.actions ?? [];\n if (actions.length > 0) {\n const lines: string[] = [];\n for (const action of actions) {\n const name = await emitter.declare(`${action.id}Input`, action.input);\n lines.push(` '${action.id}': { input: ${name} };`);\n }\n augmentations.push(` interface TapActionMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n const tools = contributes.ai?.tools ?? [];\n if (tools.length > 0) {\n const lines: string[] = [];\n for (const tool of tools) {\n const name = await emitter.declare(`${tool.id}Input`, tool.input);\n lines.push(` '${tool.id}': { input: ${name} };`);\n }\n augmentations.push(` interface TapToolMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n const prompts = (contributes.ai?.prompts ?? []).filter(\n prompt => prompt.input !== undefined,\n );\n if (prompts.length > 0) {\n const lines: string[] = [];\n for (const prompt of prompts) {\n const name = await emitter.declare(\n `${prompt.id}PromptInput`,\n prompt.input ?? { type: 'object' },\n );\n lines.push(` '${prompt.id}': { input: ${name} };`);\n }\n augmentations.push(` interface TapPromptMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n const webhooks = contributes.webhooks ?? [];\n if (webhooks.length > 0) {\n const lines: string[] = [];\n for (const webhook of webhooks) {\n const name = await emitter.declare(\n `${webhook.event}Payload`,\n webhook.payload,\n );\n lines.push(` '${webhook.event}': ${name};`);\n }\n augmentations.push(` interface TapWebhookMap {\\n${lines.join('\\n')}\\n }`);\n }\n\n if (registrySnapshot !== undefined && registrySnapshot.events.length > 0) {\n const lines: string[] = [];\n for (const event of registrySnapshot.events) {\n const name = await emitter.declare(\n `${event.name}Event`,\n event.payload ?? { type: 'object' },\n );\n lines.push(` '${event.name}': ${name};`);\n }\n augmentations.push(\n ` interface TapHostEventMap {\\n${lines.join('\\n')}\\n }`,\n );\n }\n\n const telemetry = manifest.telemetry ?? [];\n if (telemetry.length > 0) {\n const lines = telemetry.map(name => ` '${name}': true;`);\n augmentations.push(\n ` interface TapTelemetryEvents {\\n${lines.join('\\n')}\\n }`,\n );\n }\n\n if (contributes.settings) {\n const name = await emitter.declare('Settings', contributes.settings.schema);\n augmentations.push(` interface TapSettings extends ${name} {}`);\n }\n\n const credentialFields = contributes.connector?.auth?.fields ?? [];\n if (credentialFields.length > 0) {\n const name = await emitter.declare(\n 'Credentials',\n credentialSchema(credentialFields),\n );\n augmentations.push(` interface TapCredentials extends ${name} {}`);\n }\n\n const declarations = emitter.declarations();\n const module =\n augmentations.length === 0\n ? \"declare module '@tappify/extension-sdk' {}\"\n : `declare module '@tappify/extension-sdk' {\\n${augmentations.join('\\n\\n')}\\n}`;\n\n return [\n GENERATED_HEADER,\n '',\n ...(declarations.length > 0 ? [declarations.join('\\n\\n'), ''] : []),\n module,\n '',\n 'export {};',\n '',\n ].join('\\n');\n}\n","/** Every code a `TapError` the SDK raises carries. */\nexport type TapErrorCode =\n | 'TAP_OUTSIDE_HOST'\n | 'TAP_SCOPE_MISSING'\n | 'TAP_UNKNOWN_COLLECTION'\n | 'TAP_UNKNOWN_PROCEDURE'\n | 'TAP_NO_PORTAL'\n | 'TAP_NO_QUERY_CLIENT'\n | 'TAP_MANIFEST_INVALID'\n | 'TAP_ENTRY_MISSING'\n | 'TAP_STYLES_UNRESOLVED'\n | 'TAP_SCHEMA_UNRESOLVED'\n | 'TAP_TOKEN_MISSING'\n | 'TAP_TOKEN_INVALID'\n | 'TAP_TOKEN_AUDIENCE'\n | 'TAP_EVENT_ID_MISSING'\n | 'TAP_BODY_INVALID'\n | 'TAP_BODY_TOO_LARGE'\n | 'TAP_ROUTE_UNKNOWN'\n | 'TAP_HANDLER_MISSING'\n | 'TAP_INTERNAL_ERROR'\n | 'TAP_WEBHOOK_REJECTED'\n | 'TAP_TEST_TARGET_INVALID';\n\n/**\n * The same codes as `TapErrorCode`, as an array to iterate or check membership\n * against at runtime.\n */\nexport const TAP_ERROR_CODES: readonly TapErrorCode[] = [\n 'TAP_OUTSIDE_HOST',\n 'TAP_SCOPE_MISSING',\n 'TAP_UNKNOWN_COLLECTION',\n 'TAP_UNKNOWN_PROCEDURE',\n 'TAP_NO_PORTAL',\n 'TAP_NO_QUERY_CLIENT',\n 'TAP_MANIFEST_INVALID',\n 'TAP_ENTRY_MISSING',\n 'TAP_STYLES_UNRESOLVED',\n 'TAP_SCHEMA_UNRESOLVED',\n 'TAP_TOKEN_MISSING',\n 'TAP_TOKEN_INVALID',\n 'TAP_TOKEN_AUDIENCE',\n 'TAP_EVENT_ID_MISSING',\n 'TAP_BODY_INVALID',\n 'TAP_BODY_TOO_LARGE',\n 'TAP_ROUTE_UNKNOWN',\n 'TAP_HANDLER_MISSING',\n 'TAP_INTERNAL_ERROR',\n 'TAP_WEBHOOK_REJECTED',\n 'TAP_TEST_TARGET_INVALID',\n];\n\nconst CODE_SET = new Set<string>(TAP_ERROR_CODES);\n\n/**\n * The error the SDK and the host raise when a call cannot be served at all.\n *\n * @remarks\n * The SDK throws it for a mount outside the host (`TAP_OUTSIDE_HOST`), a\n * collection or procedure the manifest does not declare\n * (`TAP_UNKNOWN_COLLECTION`, `TAP_UNKNOWN_PROCEDURE`), a missing portal or query\n * client, a token the server rejected, and a misused testing helper. The host\n * turns a refused read or write into `TAP_SCOPE_MISSING`. Branch on `code`, which\n * is stable; the message is prose and changes. An error your own server raised is\n * a `TapServerError` instead.\n *\n * @example\n * ```tsx\n * import { TapError, TapErrorState, useTapQuery } from '@tappify/extension-sdk';\n *\n * function Revenue() {\n * const revenue = useTapQuery({\n * kind: 'revenue',\n * range: { from: '2026-09-01', to: '2026-09-08' },\n * });\n * if (TapError.is(revenue.error) && revenue.error.code === 'TAP_SCOPE_MISSING') {\n * return <TapErrorState title=\"Revenue is not shared with this extension\" />;\n * }\n * return <span>{revenue.data?.mrr ?? 0}</span>;\n * }\n * ```\n */\nexport class TapError extends Error {\n readonly code: TapErrorCode;\n\n constructor(code: TapErrorCode, message: string) {\n super(message);\n this.name = 'TapError';\n this.code = code;\n }\n\n /**\n * Narrows an unknown value to a `TapError`, by name and by a code it\n * recognises, so it holds across module instances.\n */\n static is(value: unknown): value is TapError {\n return (\n value instanceof Error &&\n value.name === 'TapError' &&\n 'code' in value &&\n typeof value.code === 'string' &&\n CODE_SET.has(value.code)\n );\n }\n}\n\n/**\n * The error a vendor server throws to answer a Tappify call with a code and a\n * message the owner sees.\n *\n * @remarks\n * `createTappifyHandler` catches it and answers `{ error: { code, message } }`\n * with `status`, or 400 when no status was given; the host relays both to the\n * calling mount, where they arrive as the `error` of `useTapServer`. Pick a code\n * of your own and keep it stable — the SDK's own codes are `SERVER_ERROR_CODES` —\n * and write the message for the owner, who reads it on screen.\n *\n * @example\n * ```ts\n * import { TapServerError } from '@tappify/extension-sdk/server';\n *\n * function assertRange(days: number) {\n * if (days > 0) return;\n * throw new TapServerError(\n * 'RANGE_TOO_SHORT',\n * 'getSummary needs a range of at least one day. Widen the date picker, then retry.',\n * 400,\n * );\n * }\n * ```\n */\nexport class TapServerError extends Error {\n readonly code: string;\n readonly status: number | undefined;\n\n constructor(code: string, message: string, status?: number) {\n super(message);\n this.name = 'TapServerError';\n this.code = code;\n this.status = status;\n }\n\n /** The wire body the handler answers with, without the status. */\n toJSON(): { error: { code: string; message: string } } {\n return { error: { code: this.code, message: this.message } };\n }\n\n /**\n * Narrows an unknown value to a `TapServerError` by name and by carrying a\n * string `code`, so it holds across module instances.\n */\n static is(value: unknown): value is TapServerError {\n return (\n value instanceof Error &&\n value.name === 'TapServerError' &&\n 'code' in value &&\n typeof value.code === 'string'\n );\n }\n}\n"],"mappings":"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","names":["module"]}
@@ -28,12 +28,6 @@ declare const SCOPES: readonly [{
28
28
  readonly description: "Review text, ratings and replies";
29
29
  readonly requiresSecurityReview: false;
30
30
  readonly requiresJustification: false;
31
- }, {
32
- readonly key: "review.threads:read";
33
- readonly label: "Read Review threads";
34
- readonly description: "Review threads, comments, screenshot descriptors, build context and safe device context";
35
- readonly requiresSecurityReview: false;
36
- readonly requiresJustification: false;
37
31
  }, {
38
32
  readonly key: "crashes:read";
39
33
  readonly label: "Read crash data";
@@ -97,7 +91,7 @@ declare const SCOPES: readonly [{
97
91
  }, {
98
92
  readonly key: "work:sync";
99
93
  readonly label: "Sync work items";
100
- readonly description: "Sync comments and status on created work items";
94
+ readonly description: "Two-way comments and status on created work items";
101
95
  readonly requiresSecurityReview: false;
102
96
  readonly requiresJustification: true;
103
97
  }, {
@@ -349,53 +343,26 @@ interface AiContribution {
349
343
  prompts?: PromptDeclaration[];
350
344
  context?: ContextDeclaration[];
351
345
  }
352
- type WorkDirection = 'none' | 'to_provider' | 'from_provider' | 'two-way';
353
- type WorkAuth = {
354
- method: 'oauth';
355
- provider: string;
356
- scopes: string[];
357
- } | {
358
- method: 'api_key';
359
- fields: CredentialField[];
360
- };
361
346
  interface WorkContribution {
362
347
  kind: 'tracker' | 'docs' | 'chat';
363
- creates: ('issue' | 'page' | 'message')[];
364
- identity: 'install' | 'user';
365
- comments: 'two-way' | 'none' | {
366
- direction: WorkDirection;
367
- supportsEdits: false;
368
- supportsDeletes: false;
369
- };
370
- status?: {
371
- direction: WorkDirection;
372
- webhook?: string;
373
- map?: Record<'open' | 'in_progress' | 'done', string[]>;
374
- };
375
- attachments: ('image' | 'link')[];
376
- auth: WorkAuth;
377
- /** @deprecated resolved dynamically by describe_container */
378
- container?: {
348
+ container: {
379
349
  label: string;
380
350
  list: string;
381
351
  };
382
- /** @deprecated resolved dynamically by describe_container */
383
- fields?: {
352
+ creates: ('issue' | 'page' | 'message')[];
353
+ fields: {
384
354
  priority?: string[];
385
355
  labels?: boolean;
386
356
  assignee?: boolean;
387
357
  required: string[];
388
358
  };
389
- }
390
- /** @deprecated Migrate to WorkContribution during the SDK v2 compatibility window. */
391
- interface LegacyWorkContribution extends Omit<WorkContribution, 'auth' | 'container' | 'fields' | 'comments' | 'status'> {
392
- container: NonNullable<WorkContribution['container']>;
393
- fields: NonNullable<WorkContribution['fields']>;
359
+ attachments: ('image' | 'link')[];
394
360
  comments: 'two-way' | 'none';
395
361
  status?: {
396
362
  webhook: string;
397
363
  map: Record<'open' | 'in_progress' | 'done', string[]>;
398
364
  };
365
+ identity: 'user' | 'install';
399
366
  }
400
367
  interface ExtensionListing {
401
368
  longDescription?: string;
@@ -428,7 +395,7 @@ interface ExtensionManifest {
428
395
  connector?: ConnectorContribution;
429
396
  webhooks?: WebhookContribution[];
430
397
  ai?: AiContribution;
431
- work?: WorkContribution | LegacyWorkContribution;
398
+ work?: WorkContribution;
432
399
  };
433
400
  server?: {
434
401
  baseUrl: string;