@stripe/extensibility-dev-tools 0.23.5

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 (253) hide show
  1. package/LICENSE.md +19 -0
  2. package/dist/bin/build-custom-object-definitions.cjs +2111 -0
  3. package/dist/bin/build-custom-object-definitions.d.ts +11 -0
  4. package/dist/bin/build-custom-object-definitions.d.ts.map +1 -0
  5. package/dist/bin/build-custom-object-definitions.js +2088 -0
  6. package/dist/bin/create-upload-image.cjs +2136 -0
  7. package/dist/bin/create-upload-image.d.ts +17 -0
  8. package/dist/bin/create-upload-image.d.ts.map +1 -0
  9. package/dist/bin/create-upload-image.js +2113 -0
  10. package/dist/bin/dev-tools-rpc.cjs +3874 -0
  11. package/dist/bin/dev-tools-rpc.d.ts +3 -0
  12. package/dist/bin/dev-tools-rpc.d.ts.map +1 -0
  13. package/dist/bin/dev-tools-rpc.js +3864 -0
  14. package/dist/bin/gen-schemas.cjs +20739 -0
  15. package/dist/bin/gen-schemas.d.ts +8 -0
  16. package/dist/bin/gen-schemas.d.ts.map +1 -0
  17. package/dist/bin/gen-schemas.js +20715 -0
  18. package/dist/bin/gen-workspace.cjs +3847 -0
  19. package/dist/bin/gen-workspace.d.ts +8 -0
  20. package/dist/bin/gen-workspace.d.ts.map +1 -0
  21. package/dist/bin/gen-workspace.js +3841 -0
  22. package/dist/bin/manifest.cjs +686 -0
  23. package/dist/bin/manifest.d.ts +10 -0
  24. package/dist/bin/manifest.d.ts.map +1 -0
  25. package/dist/bin/manifest.js +663 -0
  26. package/dist/bin/rpc/dispatch.d.ts +10 -0
  27. package/dist/bin/rpc/dispatch.d.ts.map +1 -0
  28. package/dist/bin/rpc/handlers.d.ts +4 -0
  29. package/dist/bin/rpc/handlers.d.ts.map +1 -0
  30. package/dist/bin/rpc/types.d.ts +29 -0
  31. package/dist/bin/rpc/types.d.ts.map +1 -0
  32. package/dist/bin/template-info.cjs +1511 -0
  33. package/dist/bin/template-info.d.ts +9 -0
  34. package/dist/bin/template-info.d.ts.map +1 -0
  35. package/dist/bin/template-info.js +1488 -0
  36. package/dist/custom-objects/build-definitions.d.ts +98 -0
  37. package/dist/custom-objects/build-definitions.d.ts.map +1 -0
  38. package/dist/custom-objects/generated/proto/custom_objects/pub/api/app_api/object_definitions_app_service.pb.d.ts +191 -0
  39. package/dist/custom-objects/generated/proto/custom_objects/pub/api/app_api/object_definitions_app_service.pb.d.ts.map +1 -0
  40. package/dist/custom-objects/generated/proto/custom_objects/pub/api/common/schema.pb.d.ts +131 -0
  41. package/dist/custom-objects/generated/proto/custom_objects/pub/api/common/schema.pb.d.ts.map +1 -0
  42. package/dist/custom-objects/generated/proto/google/protobuf/descriptor.pb.d.ts +1482 -0
  43. package/dist/custom-objects/generated/proto/google/protobuf/descriptor.pb.d.ts.map +1 -0
  44. package/dist/custom-objects/generated/proto/google/protobuf/timestamp.pb.d.ts +167 -0
  45. package/dist/custom-objects/generated/proto/google/protobuf/timestamp.pb.d.ts.map +1 -0
  46. package/dist/custom-objects/generated/proto/proto/annotations.pb.d.ts +64 -0
  47. package/dist/custom-objects/generated/proto/proto/annotations.pb.d.ts.map +1 -0
  48. package/dist/custom-objects/generated/proto/proto/extensions.pb.d.ts +657 -0
  49. package/dist/custom-objects/generated/proto/proto/extensions.pb.d.ts.map +1 -0
  50. package/dist/custom-objects/generated/proto/vendor/metadata/pub/api/api_metadata.pb.d.ts +105 -0
  51. package/dist/custom-objects/generated/proto/vendor/metadata/pub/api/api_metadata.pb.d.ts.map +1 -0
  52. package/dist/custom-objects/generated/proto/vendor/net/idempotency/idempotency_model.pb.d.ts +79 -0
  53. package/dist/custom-objects/generated/proto/vendor/net/idempotency/idempotency_model.pb.d.ts.map +1 -0
  54. package/dist/custom-objects/generated/proto/vendor/publicapi/api_group_enum.pb.d.ts +129 -0
  55. package/dist/custom-objects/generated/proto/vendor/publicapi/api_group_enum.pb.d.ts.map +1 -0
  56. package/dist/custom-objects/generated/proto/vendor/publicapi/api_visibility.pb.d.ts +76 -0
  57. package/dist/custom-objects/generated/proto/vendor/publicapi/api_visibility.pb.d.ts.map +1 -0
  58. package/dist/custom-objects/generated/proto/vendor/publicapi/customize_dispatch_middleware_enum.pb.d.ts +78 -0
  59. package/dist/custom-objects/generated/proto/vendor/publicapi/customize_dispatch_middleware_enum.pb.d.ts.map +1 -0
  60. package/dist/custom-objects/generated/proto/vendor/publicapi/docs_namespace_group_enum.pb.d.ts +146 -0
  61. package/dist/custom-objects/generated/proto/vendor/publicapi/docs_namespace_group_enum.pb.d.ts.map +1 -0
  62. package/dist/custom-objects/generated/proto/vendor/publicapi/documented_enum.pb.d.ts +76 -0
  63. package/dist/custom-objects/generated/proto/vendor/publicapi/documented_enum.pb.d.ts.map +1 -0
  64. package/dist/custom-objects/generated/proto/vendor/publicapi/event_scope_enum.pb.d.ts +92 -0
  65. package/dist/custom-objects/generated/proto/vendor/publicapi/event_scope_enum.pb.d.ts.map +1 -0
  66. package/dist/custom-objects/generated/proto/vendor/publicapi/extension_interface.pb.d.ts +124 -0
  67. package/dist/custom-objects/generated/proto/vendor/publicapi/extension_interface.pb.d.ts.map +1 -0
  68. package/dist/custom-objects/generated/proto/vendor/publicapi/feature_enum.pb.d.ts +1070 -0
  69. package/dist/custom-objects/generated/proto/vendor/publicapi/feature_enum.pb.d.ts.map +1 -0
  70. package/dist/custom-objects/generated/proto/vendor/publicapi/field_validation_rules.pb.d.ts +279 -0
  71. package/dist/custom-objects/generated/proto/vendor/publicapi/field_validation_rules.pb.d.ts.map +1 -0
  72. package/dist/custom-objects/generated/proto/vendor/publicapi/flavor_enum.pb.d.ts +78 -0
  73. package/dist/custom-objects/generated/proto/vendor/publicapi/flavor_enum.pb.d.ts.map +1 -0
  74. package/dist/custom-objects/generated/proto/vendor/publicapi/http_error_status.pb.d.ts +102 -0
  75. package/dist/custom-objects/generated/proto/vendor/publicapi/http_error_status.pb.d.ts.map +1 -0
  76. package/dist/custom-objects/generated/proto/vendor/publicapi/method_kind_enum.pb.d.ts +86 -0
  77. package/dist/custom-objects/generated/proto/vendor/publicapi/method_kind_enum.pb.d.ts.map +1 -0
  78. package/dist/custom-objects/generated/proto/vendor/publicapi/method_priority.pb.d.ts +80 -0
  79. package/dist/custom-objects/generated/proto/vendor/publicapi/method_priority.pb.d.ts.map +1 -0
  80. package/dist/custom-objects/generated/proto/vendor/publicapi/permission_check_enum.pb.d.ts +74 -0
  81. package/dist/custom-objects/generated/proto/vendor/publicapi/permission_check_enum.pb.d.ts.map +1 -0
  82. package/dist/custom-objects/generated/proto/vendor/publicapi/redaction_enum.pb.d.ts +76 -0
  83. package/dist/custom-objects/generated/proto/vendor/publicapi/redaction_enum.pb.d.ts.map +1 -0
  84. package/dist/custom-objects/generated/proto/vendor/publicapi/region_routers.pb.d.ts +103 -0
  85. package/dist/custom-objects/generated/proto/vendor/publicapi/region_routers.pb.d.ts.map +1 -0
  86. package/dist/custom-objects/generated/proto/vendor/publicapi/rollout_configs.pb.d.ts +153 -0
  87. package/dist/custom-objects/generated/proto/vendor/publicapi/rollout_configs.pb.d.ts.map +1 -0
  88. package/dist/custom-objects/generated/proto/vendor/publicapi/v2ext.pb.d.ts +1111 -0
  89. package/dist/custom-objects/generated/proto/vendor/publicapi/v2ext.pb.d.ts.map +1 -0
  90. package/dist/custom-objects/generated/proto/vendor/vext/annotations.pb.d.ts +602 -0
  91. package/dist/custom-objects/generated/proto/vendor/vext/annotations.pb.d.ts.map +1 -0
  92. package/dist/custom-objects/generated/proto/vendor/vext/extensions.pb.d.ts +144 -0
  93. package/dist/custom-objects/generated/proto/vendor/vext/extensions.pb.d.ts.map +1 -0
  94. package/dist/custom-objects/generated/proto/vendor/vext/privacy_unified_annotations.pb.d.ts +851 -0
  95. package/dist/custom-objects/generated/proto/vendor/vext/privacy_unified_annotations.pb.d.ts.map +1 -0
  96. package/dist/custom-objects/generated/proto/vendor/vext/xml_annotations.pb.d.ts +125 -0
  97. package/dist/custom-objects/generated/proto/vendor/vext/xml_annotations.pb.d.ts.map +1 -0
  98. package/dist/custom-objects/to-proto-json.d.ts +17 -0
  99. package/dist/custom-objects/to-proto-json.d.ts.map +1 -0
  100. package/dist/dependencies/index.cjs +601 -0
  101. package/dist/dependencies/index.d.ts +320 -0
  102. package/dist/dependencies/index.d.ts.map +1 -0
  103. package/dist/dependencies/index.js +560 -0
  104. package/dist/extensibility-dev-tools-alpha.d.ts +199 -0
  105. package/dist/extensibility-dev-tools-beta.d.ts +199 -0
  106. package/dist/extensibility-dev-tools-dependencies-alpha.d.ts +51 -0
  107. package/dist/extensibility-dev-tools-dependencies-beta.d.ts +51 -0
  108. package/dist/extensibility-dev-tools-dependencies-internal.d.ts +372 -0
  109. package/dist/extensibility-dev-tools-dependencies-public.d.ts +51 -0
  110. package/dist/extensibility-dev-tools-internal.d.ts +1722 -0
  111. package/dist/extensibility-dev-tools-jsonschema-tools-alpha.d.ts +57 -0
  112. package/dist/extensibility-dev-tools-jsonschema-tools-beta.d.ts +57 -0
  113. package/dist/extensibility-dev-tools-jsonschema-tools-internal.d.ts +123 -0
  114. package/dist/extensibility-dev-tools-jsonschema-tools-public.d.ts +57 -0
  115. package/dist/extensibility-dev-tools-manifest-alpha.d.ts +31 -0
  116. package/dist/extensibility-dev-tools-manifest-beta.d.ts +31 -0
  117. package/dist/extensibility-dev-tools-manifest-internal.d.ts +461 -0
  118. package/dist/extensibility-dev-tools-manifest-public.d.ts +31 -0
  119. package/dist/extensibility-dev-tools-public.d.ts +199 -0
  120. package/dist/extensibility-dev-tools-schemas-alpha.d.ts +9 -0
  121. package/dist/extensibility-dev-tools-schemas-beta.d.ts +9 -0
  122. package/dist/extensibility-dev-tools-schemas-internal.d.ts +41 -0
  123. package/dist/extensibility-dev-tools-schemas-public.d.ts +9 -0
  124. package/dist/extensibility-dev-tools-templates-alpha.d.ts +67 -0
  125. package/dist/extensibility-dev-tools-templates-beta.d.ts +67 -0
  126. package/dist/extensibility-dev-tools-templates-internal.d.ts +554 -0
  127. package/dist/extensibility-dev-tools-templates-public.d.ts +67 -0
  128. package/dist/extensibility-dev-tools-workspace-alpha.d.ts +51 -0
  129. package/dist/extensibility-dev-tools-workspace-beta.d.ts +51 -0
  130. package/dist/extensibility-dev-tools-workspace-internal.d.ts +410 -0
  131. package/dist/extensibility-dev-tools-workspace-public.d.ts +51 -0
  132. package/dist/index.cjs +3810 -0
  133. package/dist/index.d.ts +18 -0
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/index.js +3758 -0
  136. package/dist/jsonschema-tools.cjs +20451 -0
  137. package/dist/jsonschema-tools.d.ts +98 -0
  138. package/dist/jsonschema-tools.d.ts.map +1 -0
  139. package/dist/jsonschema-tools.js +20404 -0
  140. package/dist/manifest/index.cjs +610 -0
  141. package/dist/manifest/index.d.ts +8 -0
  142. package/dist/manifest/index.d.ts.map +1 -0
  143. package/dist/manifest/index.js +571 -0
  144. package/dist/manifest/manifest-v1.d.ts +102 -0
  145. package/dist/manifest/manifest-v1.d.ts.map +1 -0
  146. package/dist/manifest/manifest-v2.d.ts +253 -0
  147. package/dist/manifest/manifest-v2.d.ts.map +1 -0
  148. package/dist/manifest/stripe-app-manifest.d.ts +114 -0
  149. package/dist/manifest/stripe-app-manifest.d.ts.map +1 -0
  150. package/dist/schemas/index.cjs +20692 -0
  151. package/dist/schemas/index.d.ts +37 -0
  152. package/dist/schemas/index.d.ts.map +1 -0
  153. package/dist/schemas/index.js +20656 -0
  154. package/dist/templates/diff-viewer/diff-generator.d.ts +22 -0
  155. package/dist/templates/diff-viewer/diff-generator.d.ts.map +1 -0
  156. package/dist/templates/diff-viewer/diff-prompt.d.ts +13 -0
  157. package/dist/templates/diff-viewer/diff-prompt.d.ts.map +1 -0
  158. package/dist/templates/diff-viewer/index.d.ts +7 -0
  159. package/dist/templates/diff-viewer/index.d.ts.map +1 -0
  160. package/dist/templates/diff-viewer/terminal-renderer.d.ts +29 -0
  161. package/dist/templates/diff-viewer/terminal-renderer.d.ts.map +1 -0
  162. package/dist/templates/diff-viewer/types.d.ts +58 -0
  163. package/dist/templates/diff-viewer/types.d.ts.map +1 -0
  164. package/dist/templates/extensions/base.d.ts +23 -0
  165. package/dist/templates/extensions/base.d.ts.map +1 -0
  166. package/dist/templates/extensions/billing.bill.discount_calculation.d.ts +6 -0
  167. package/dist/templates/extensions/billing.bill.discount_calculation.d.ts.map +1 -0
  168. package/dist/templates/extensions/billing.customer_balance_application.d.ts +6 -0
  169. package/dist/templates/extensions/billing.customer_balance_application.d.ts.map +1 -0
  170. package/dist/templates/extensions/billing.invoice_collection_setting.d.ts +6 -0
  171. package/dist/templates/extensions/billing.invoice_collection_setting.d.ts.map +1 -0
  172. package/dist/templates/extensions/billing.prorations.d.ts +6 -0
  173. package/dist/templates/extensions/billing.prorations.d.ts.map +1 -0
  174. package/dist/templates/extensions/billing.recurring_billing_item_handling.d.ts +6 -0
  175. package/dist/templates/extensions/billing.recurring_billing_item_handling.d.ts.map +1 -0
  176. package/dist/templates/extensions/core.workflows.custom_action.d.ts +6 -0
  177. package/dist/templates/extensions/core.workflows.custom_action.d.ts.map +1 -0
  178. package/dist/templates/extensions/extend.objects.custom_objects.d.ts +6 -0
  179. package/dist/templates/extensions/extend.objects.custom_objects.d.ts.map +1 -0
  180. package/dist/templates/extensions/extend.workflows.custom_action.d.ts +6 -0
  181. package/dist/templates/extensions/extend.workflows.custom_action.d.ts.map +1 -0
  182. package/dist/templates/extensions/index.d.ts +13 -0
  183. package/dist/templates/extensions/index.d.ts.map +1 -0
  184. package/dist/templates/extensions/registry.d.ts +10 -0
  185. package/dist/templates/extensions/registry.d.ts.map +1 -0
  186. package/dist/templates/extensions/types.d.ts +104 -0
  187. package/dist/templates/extensions/types.d.ts.map +1 -0
  188. package/dist/templates/file-writer.d.ts +140 -0
  189. package/dist/templates/file-writer.d.ts.map +1 -0
  190. package/dist/templates/fs/_impl.d.ts +29 -0
  191. package/dist/templates/fs/_impl.d.ts.map +1 -0
  192. package/dist/templates/fs/filesystem.d.ts +8 -0
  193. package/dist/templates/fs/filesystem.d.ts.map +1 -0
  194. package/dist/templates/fs/in-memory.d.ts +9 -0
  195. package/dist/templates/fs/in-memory.d.ts.map +1 -0
  196. package/dist/templates/fs/index.d.ts +25 -0
  197. package/dist/templates/fs/index.d.ts.map +1 -0
  198. package/dist/templates/fs-utils.d.ts +17 -0
  199. package/dist/templates/fs-utils.d.ts.map +1 -0
  200. package/dist/templates/index.cjs +2248 -0
  201. package/dist/templates/index.d.ts +32 -0
  202. package/dist/templates/index.d.ts.map +1 -0
  203. package/dist/templates/index.js +2203 -0
  204. package/dist/templates/root/index.d.ts +60 -0
  205. package/dist/templates/root/index.d.ts.map +1 -0
  206. package/dist/templates/simple-templates.d.ts +8 -0
  207. package/dist/templates/simple-templates.d.ts.map +1 -0
  208. package/dist/templates/template-manager.d.ts +8 -0
  209. package/dist/templates/template-manager.d.ts.map +1 -0
  210. package/dist/templates/types.d.ts +9 -0
  211. package/dist/templates/types.d.ts.map +1 -0
  212. package/dist/tsconfig.build.tsbuildinfo +1 -0
  213. package/dist/workspace/index.cjs +3756 -0
  214. package/dist/workspace/index.d.ts +336 -0
  215. package/dist/workspace/index.d.ts.map +1 -0
  216. package/dist/workspace/index.js +3731 -0
  217. package/package.json +137 -0
  218. package/templates/extensions/billing.bill.discount_calculation/index.test.ts +15 -0
  219. package/templates/extensions/billing.bill.discount_calculation/index.ts +20 -0
  220. package/templates/extensions/billing.customer_balance_application/index.test.ts +15 -0
  221. package/templates/extensions/billing.customer_balance_application/index.ts +18 -0
  222. package/templates/extensions/billing.invoice_collection_setting/index.test.ts +15 -0
  223. package/templates/extensions/billing.invoice_collection_setting/index.ts +16 -0
  224. package/templates/extensions/billing.prorations/index.test.ts +15 -0
  225. package/templates/extensions/billing.prorations/index.ts +18 -0
  226. package/templates/extensions/billing.recurring_billing_item_handling/index.test.ts +15 -0
  227. package/templates/extensions/billing.recurring_billing_item_handling/index.ts +42 -0
  228. package/templates/extensions/common/.prettierignore +3 -0
  229. package/templates/extensions/common/eslint.config.mts.mustache +95 -0
  230. package/templates/extensions/common/package.json.mustache +26 -0
  231. package/templates/extensions/common/tsconfig.build.json.mustache +15 -0
  232. package/templates/extensions/common/tsconfig.json.mustache +16 -0
  233. package/templates/extensions/core.workflows.custom_action/custom_input.schema.json +6 -0
  234. package/templates/extensions/core.workflows.custom_action/index.test.ts +15 -0
  235. package/templates/extensions/core.workflows.custom_action/index.ts +31 -0
  236. package/templates/extensions/extend.workflows.custom_action/custom_input.schema.json +6 -0
  237. package/templates/extensions/extend.workflows.custom_action/index.test.ts +15 -0
  238. package/templates/extensions/extend.workflows.custom_action/index.ts +31 -0
  239. package/templates/root/.husky/pre-commit +1 -0
  240. package/templates/root/.prettierignore +5 -0
  241. package/templates/root/.prettierrc +7 -0
  242. package/templates/root/_gitignore +28 -0
  243. package/templates/root/custom-objects/package.json +20 -0
  244. package/templates/root/custom-objects/tsconfig.json +9 -0
  245. package/templates/root/eslint.config.mts +95 -0
  246. package/templates/root/package.json.mustache +32 -0
  247. package/templates/root/pnpm-workspace.yaml +7 -0
  248. package/templates/root/stripe-app.yaml.mustache +6 -0
  249. package/templates/root/tools/test.mts +38 -0
  250. package/templates/root/tsconfig.base.json +23 -0
  251. package/templates/root/tsconfig.json +15 -0
  252. package/templates/root/ui/package.json +17 -0
  253. package/templates/root/vitest.config.mts +47 -0
@@ -0,0 +1,2136 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/bin/create-upload-image.ts
27
+ var import_node_child_process2 = require("child_process");
28
+ var fs3 = __toESM(require("fs"), 1);
29
+ var os2 = __toESM(require("os"), 1);
30
+ var path3 = __toESM(require("path"), 1);
31
+
32
+ // src/custom-objects/build-definitions.ts
33
+ var fs2 = __toESM(require("fs"), 1);
34
+ var path2 = __toESM(require("path"), 1);
35
+ var customObjectTools = __toESM(require("@stripe/extensibility-custom-objects-tools/internal"), 1);
36
+
37
+ // src/manifest/stripe-app-manifest.ts
38
+ var import_extensibility_tool_utils2 = require("@stripe/extensibility-tool-utils");
39
+
40
+ // src/manifest/manifest-v2.ts
41
+ var import_promises = require("fs/promises");
42
+ var yaml = __toESM(require("yaml"), 1);
43
+ var import_extensibility_tool_utils = require("@stripe/extensibility-tool-utils");
44
+ function isNonEmptyString(value) {
45
+ return typeof value === "string" && value.trim().length > 0;
46
+ }
47
+ function isYamlObject(value) {
48
+ return typeof value === "object" && value !== null && !Array.isArray(value);
49
+ }
50
+ function validateManifestV2Data(data) {
51
+ if (!isNonEmptyString(data.id)) {
52
+ throw new Error('Manifest must contain non-empty "id" field');
53
+ }
54
+ if (!isNonEmptyString(data.name)) {
55
+ throw new Error('Manifest must contain non-empty "name" field');
56
+ }
57
+ if (!isNonEmptyString(data.version)) {
58
+ throw new Error('Manifest must contain non-empty "version" field');
59
+ }
60
+ if ("extensions" in data && !Array.isArray(data.extensions)) {
61
+ throw new Error('Manifest field "extensions" must be an array when provided');
62
+ }
63
+ if ("custom_object_definitions" in data && !isYamlObject(data.custom_object_definitions)) {
64
+ throw new Error(
65
+ 'Manifest field "custom_object_definitions" must be an object with a "definitions" array'
66
+ );
67
+ }
68
+ const coBlock = data.custom_object_definitions;
69
+ const definitions = isYamlObject(coBlock) ? coBlock.definitions : void 0;
70
+ if (!definitions) {
71
+ return;
72
+ }
73
+ if (!Array.isArray(definitions)) {
74
+ throw new Error(
75
+ 'Manifest field "custom_object_definitions.definitions" must be an array'
76
+ );
77
+ }
78
+ for (let i = 0; i < definitions.length; i++) {
79
+ const def = definitions[i];
80
+ const prefix = `custom_object_definitions.definitions[${String(i)}]`;
81
+ if (!isYamlObject(def) || !isNonEmptyString(def.id)) {
82
+ throw new Error(
83
+ `${prefix}: missing or invalid "id" field. Got: ${JSON.stringify(def)}`
84
+ );
85
+ }
86
+ if (!isYamlObject(def.specification)) {
87
+ throw new Error(
88
+ `${prefix}: missing or invalid "specification" field. Expected: { type: "typescript", content: "path/to/file.ts" }. Got: ${JSON.stringify(def)}`
89
+ );
90
+ }
91
+ if (!isNonEmptyString(def.specification.type)) {
92
+ throw new Error(
93
+ `${prefix}: missing or invalid "specification.type" field. Got: ${JSON.stringify(def)}`
94
+ );
95
+ }
96
+ if (!isNonEmptyString(def.specification.content)) {
97
+ throw new Error(
98
+ `${prefix}: missing or invalid "specification.content" field. Got: ${JSON.stringify(def)}`
99
+ );
100
+ }
101
+ }
102
+ }
103
+ var _ManifestV2 = class __ManifestV2 {
104
+ constructor(filePath, data) {
105
+ this.filePath = filePath;
106
+ this.data = data;
107
+ }
108
+ /**
109
+ * Load stripe-app.yaml from disk
110
+ * @param filePath - Path to stripe-app.yaml
111
+ * @throws Error if file is invalid or cannot be read
112
+ */
113
+ static async load(filePath) {
114
+ try {
115
+ const content = await (0, import_promises.readFile)(filePath, "utf-8");
116
+ const parsed = yaml.parse(content);
117
+ if (!isYamlObject(parsed)) {
118
+ throw new Error("Invalid stripe-app.yaml format: root must be an object");
119
+ }
120
+ validateManifestV2Data(parsed);
121
+ const data = parsed;
122
+ data.extensions ??= [];
123
+ return new __ManifestV2(filePath, data);
124
+ } catch (err) {
125
+ if (err instanceof Error) {
126
+ throw new Error(`Error reading stripe-app.yaml: ${err.message}`);
127
+ }
128
+ throw new Error(`Error reading stripe-app.yaml: ${String(err)}`);
129
+ }
130
+ }
131
+ /**
132
+ * Get the app ID from the manifest
133
+ */
134
+ getAppId() {
135
+ return this.data.id;
136
+ }
137
+ /**
138
+ * Get the app name from the manifest
139
+ */
140
+ getAppName() {
141
+ return this.data.name;
142
+ }
143
+ // ============================================================
144
+ // Custom Objects
145
+ // ============================================================
146
+ /**
147
+ * Get all custom object definitions, parsed into internal format.
148
+ * Entries are guaranteed structurally valid by load-time validation.
149
+ */
150
+ getCustomObjects() {
151
+ const definitions = this.data.custom_object_definitions?.definitions;
152
+ if (!definitions || definitions.length === 0) {
153
+ return [];
154
+ }
155
+ return definitions.map((def) => ({
156
+ id: def.id,
157
+ type: def.specification.type,
158
+ path: def.specification.content,
159
+ name: (0, import_extensibility_tool_utils._toPascalCase)(def.id)
160
+ }));
161
+ }
162
+ /**
163
+ * Check if a custom object exists by id
164
+ */
165
+ hasCustomObject(id) {
166
+ const definitions = this.data.custom_object_definitions?.definitions;
167
+ if (!definitions) return false;
168
+ return definitions.some((def) => def.id === id);
169
+ }
170
+ /**
171
+ * Add a custom object definition.
172
+ *
173
+ * Identity is keyed on `id` alone. The export name is derived via
174
+ * `_toPascalCase(id)` at read time, so `id` should be valid snake_case
175
+ * (e.g., "device", "loyalty_card", "http_request").
176
+ *
177
+ * @param id - Object identifier (e.g., "device", "loyalty_card")
178
+ * @param content - Path to the definition file (e.g., "src/device.object.ts")
179
+ * @param type - Specification type, defaults to "typescript"
180
+ * @returns true if added, false if already exists
181
+ */
182
+ addCustomObject(id, content, type = "typescript") {
183
+ if (this.hasCustomObject(id)) {
184
+ return false;
185
+ }
186
+ this.data.custom_object_definitions ??= {};
187
+ this.data.custom_object_definitions.definitions ??= [];
188
+ this.data.custom_object_definitions.definitions.push({
189
+ id,
190
+ specification: { type, content }
191
+ });
192
+ return true;
193
+ }
194
+ /**
195
+ * Remove a custom object definition by id
196
+ * @param id - Object identifier to remove
197
+ * @returns true if removed, false if not found
198
+ */
199
+ removeCustomObject(id) {
200
+ const block = this.data.custom_object_definitions;
201
+ if (!block?.definitions) {
202
+ return false;
203
+ }
204
+ const originalLength = block.definitions.length;
205
+ block.definitions = block.definitions.filter((def) => def.id !== id);
206
+ const newLength = block.definitions.length;
207
+ if (newLength === 0) {
208
+ delete this.data.custom_object_definitions;
209
+ }
210
+ return newLength < originalLength;
211
+ }
212
+ // ============================================================
213
+ // Extensions
214
+ // ============================================================
215
+ /**
216
+ * Add a new extension or update an existing one by ID
217
+ * If an extension with the provided ID exists, it will be replaced entirely
218
+ * @param extension - Extension configuration with all required fields including id
219
+ * @returns The extension ID
220
+ */
221
+ addOrUpdateExtension(extension) {
222
+ const newExtension = {
223
+ id: extension.id,
224
+ name: extension.name,
225
+ interface_id: extension.interface_id,
226
+ version: extension.version,
227
+ ...extension.description !== void 0 && {
228
+ description: extension.description
229
+ },
230
+ ...extension.stripe_version !== void 0 && {
231
+ stripe_version: extension.stripe_version
232
+ },
233
+ ...extension.script_entry_point !== void 0 && {
234
+ script_entry_point: extension.script_entry_point
235
+ },
236
+ ...extension.script !== void 0 && { script: extension.script },
237
+ permissions: extension.permissions,
238
+ methods: extension.methods,
239
+ ...extension.configuration !== void 0 && {
240
+ configuration: extension.configuration
241
+ },
242
+ ...extension.endpoints !== void 0 && {
243
+ endpoints: extension.endpoints
244
+ }
245
+ };
246
+ this.data.extensions ??= [];
247
+ const existingIndex = this.data.extensions.findIndex(
248
+ (ext) => ext.id === extension.id
249
+ );
250
+ if (existingIndex !== -1) {
251
+ this.data.extensions[existingIndex] = newExtension;
252
+ } else {
253
+ this.data.extensions.push(newExtension);
254
+ }
255
+ return extension.id;
256
+ }
257
+ /**
258
+ * Get a read-only view of all extensions
259
+ */
260
+ getExtensions() {
261
+ return this.data.extensions ?? [];
262
+ }
263
+ /**
264
+ * Get the raw manifest data (for reading other fields)
265
+ */
266
+ getRawData() {
267
+ return this.data;
268
+ }
269
+ /**
270
+ * Save changes back to disk
271
+ * @throws Error if file cannot be written
272
+ */
273
+ async save() {
274
+ try {
275
+ const updatedContent = yaml.stringify(this.data);
276
+ await (0, import_promises.writeFile)(this.filePath, updatedContent, "utf-8");
277
+ } catch (err) {
278
+ if (err instanceof Error) {
279
+ throw new Error(`Error writing stripe-app.yaml: ${err.message}`);
280
+ }
281
+ throw new Error(`Error writing stripe-app.yaml: ${String(err)}`);
282
+ }
283
+ }
284
+ };
285
+
286
+ // src/templates/template-manager.ts
287
+ var import_extensibility_tool_utils3 = require("@stripe/extensibility-tool-utils");
288
+
289
+ // src/templates/extensions/types.ts
290
+ var _ExtensionTemplateManager = class extends import_extensibility_tool_utils3._TemplateManager {
291
+ /**
292
+ * Get summary info for all registered extension templates
293
+ */
294
+ getTemplateInfo() {
295
+ return this.getTemplateEntries().map(([key, t]) => ({
296
+ key,
297
+ description: t.description,
298
+ deprecated: t.deprecated ?? false,
299
+ hidden: t.hidden ?? false,
300
+ methods: t.methods
301
+ }));
302
+ }
303
+ };
304
+
305
+ // src/templates/fs/in-memory.ts
306
+ var import_extensibility_tool_utils4 = require("@stripe/extensibility-tool-utils");
307
+
308
+ // templates-virtual:./_impl.js
309
+ var TEMPLATE_FS_IMAGE = [
310
+ {
311
+ path: "extensions/billing.bill.discount_calculation/index.test.ts",
312
+ content: `import { beforeEach, describe, it, expect } from 'vitest';
313
+
314
+ import MyDiscountCalculation from './index.js';
315
+
316
+ describe('MyDiscountCalculation', () => {
317
+ let instance: MyDiscountCalculation;
318
+
319
+ beforeEach(() => {
320
+ instance = new MyDiscountCalculation();
321
+ });
322
+
323
+ it('should be constructable', () => {
324
+ expect(instance).toBeInstanceOf(MyDiscountCalculation);
325
+ });
326
+ });
327
+ `
328
+ },
329
+ {
330
+ path: "extensions/billing.bill.discount_calculation/index.ts",
331
+ content: `import type { Billing } from '@stripe/extensibility-sdk/extensions';
332
+ import type { Context } from '@stripe/extensibility-sdk/extensions';
333
+
334
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
335
+ interface MyDiscountCalculationConfig extends Record<string, unknown> {}
336
+
337
+ export default class MyDiscountCalculation implements Billing.Bill
338
+ .DiscountCalculation<MyDiscountCalculationConfig> {
339
+ computeDiscounts(
340
+ request: Billing.Bill.DiscountCalculation.DiscountableItem,
341
+ _config: MyDiscountCalculationConfig,
342
+ _context: Context
343
+ ) {
344
+ // TODO: implement your discount logic here
345
+
346
+ return {
347
+ discount: { amount: request.grossAmount },
348
+ };
349
+ }
350
+ }
351
+ `
352
+ },
353
+ {
354
+ path: "extensions/billing.customer_balance_application/index.test.ts",
355
+ content: `import { beforeEach, describe, it, expect } from 'vitest';
356
+
357
+ import MyCustomerBalanceApplication from './index.js';
358
+
359
+ describe('MyCustomerBalanceApplication', () => {
360
+ let instance: MyCustomerBalanceApplication;
361
+
362
+ beforeEach(() => {
363
+ instance = new MyCustomerBalanceApplication();
364
+ });
365
+
366
+ it('should be constructable', () => {
367
+ expect(instance).toBeInstanceOf(MyCustomerBalanceApplication);
368
+ });
369
+ });
370
+ `
371
+ },
372
+ {
373
+ path: "extensions/billing.customer_balance_application/index.ts",
374
+ content: `import type { Billing, Context } from '@stripe/extensibility-sdk/extensions';
375
+
376
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
377
+ interface MyCustomerBalanceApplicationConfig extends Record<string, unknown> {}
378
+
379
+ export default class MyCustomerBalanceApplication implements Billing.CustomerBalanceApplication<MyCustomerBalanceApplicationConfig> {
380
+ computeAppliedCustomerBalance(
381
+ request: Billing.CustomerBalanceApplication.CustomerBalanceApplicationInput,
382
+ _config: MyCustomerBalanceApplicationConfig,
383
+ _context: Context
384
+ ) {
385
+ // TODO: implement your customer balance logic here
386
+
387
+ return {
388
+ appliedCustomerBalance: request.customerBalance,
389
+ };
390
+ }
391
+ }
392
+ `
393
+ },
394
+ {
395
+ path: "extensions/billing.invoice_collection_setting/index.test.ts",
396
+ content: `import { beforeEach, describe, it, expect } from 'vitest';
397
+
398
+ import MyInvoiceCollectionSetting from './index.js';
399
+
400
+ describe('MyInvoiceCollectionSetting', () => {
401
+ let instance: MyInvoiceCollectionSetting;
402
+
403
+ beforeEach(() => {
404
+ instance = new MyInvoiceCollectionSetting();
405
+ });
406
+
407
+ it('should be constructable', () => {
408
+ expect(instance).toBeInstanceOf(MyInvoiceCollectionSetting);
409
+ });
410
+ });
411
+ `
412
+ },
413
+ {
414
+ path: "extensions/billing.invoice_collection_setting/index.ts",
415
+ content: `import type { Billing, Context } from '@stripe/extensibility-sdk/extensions';
416
+
417
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
418
+ interface MyInvoiceCollectionSettingConfig extends Record<string, unknown> {}
419
+
420
+ export default class MyInvoiceCollectionSetting implements Billing.InvoiceCollectionSetting<MyInvoiceCollectionSettingConfig> {
421
+ collectionOverride(
422
+ _request: Billing.InvoiceCollectionSetting.InvoiceCollectionRequest,
423
+ _config: MyInvoiceCollectionSettingConfig,
424
+ _context: Context
425
+ ) {
426
+ // TODO: implement your collection setting logic here
427
+
428
+ return {};
429
+ }
430
+ }
431
+ `
432
+ },
433
+ {
434
+ path: "extensions/billing.prorations/index.test.ts",
435
+ content: `import { beforeEach, describe, it, expect } from 'vitest';
436
+
437
+ import MyProrations from './index.js';
438
+
439
+ describe('MyProrations', () => {
440
+ let instance: MyProrations;
441
+
442
+ beforeEach(() => {
443
+ instance = new MyProrations();
444
+ });
445
+
446
+ it('should be constructable', () => {
447
+ expect(instance).toBeInstanceOf(MyProrations);
448
+ });
449
+ });
450
+ `
451
+ },
452
+ {
453
+ path: "extensions/billing.prorations/index.ts",
454
+ content: `import type { Billing, Context } from '@stripe/extensibility-sdk/extensions';
455
+
456
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
457
+ interface MyProrationsConfig extends Record<string, unknown> {}
458
+
459
+ export default class MyProrations implements Billing.Prorations<MyProrationsConfig> {
460
+ prorateItems(
461
+ _request: Billing.Prorations.ProrateItemsInput,
462
+ _config: MyProrationsConfig,
463
+ _context: Context
464
+ ) {
465
+ // TODO: implement your proration logic here
466
+
467
+ return {
468
+ items: [],
469
+ };
470
+ }
471
+ }
472
+ `
473
+ },
474
+ {
475
+ path: "extensions/billing.recurring_billing_item_handling/index.test.ts",
476
+ content: `import { beforeEach, describe, it, expect } from 'vitest';
477
+
478
+ import MyRecurringBillingItemHandling from './index.js';
479
+
480
+ describe('MyRecurringBillingItemHandling', () => {
481
+ let instance: MyRecurringBillingItemHandling;
482
+
483
+ beforeEach(() => {
484
+ instance = new MyRecurringBillingItemHandling();
485
+ });
486
+
487
+ it('should be constructable', () => {
488
+ expect(instance).toBeInstanceOf(MyRecurringBillingItemHandling);
489
+ });
490
+ });
491
+ `
492
+ },
493
+ {
494
+ path: "extensions/billing.recurring_billing_item_handling/index.ts",
495
+ content: `import type { Billing, Context } from '@stripe/extensibility-sdk/extensions';
496
+
497
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
498
+ interface MyRecurringBillingItemHandlingConfig extends Record<string, unknown> {}
499
+
500
+ export default class MyRecurringBillingItemHandling implements Billing.RecurringBillingItemHandling<MyRecurringBillingItemHandlingConfig> {
501
+ beforeItemCreation(
502
+ _request: Billing.RecurringBillingItemHandling.BeforeItemCreationInput,
503
+ _config: MyRecurringBillingItemHandlingConfig,
504
+ _context: Context
505
+ ) {
506
+ // TODO: implement your before-item-creation logic here
507
+
508
+ return {
509
+ items: [],
510
+ };
511
+ }
512
+
513
+ filterItems(
514
+ _request: Billing.RecurringBillingItemHandling.FilterItemsInput,
515
+ _config: MyRecurringBillingItemHandlingConfig,
516
+ _context: Context
517
+ ) {
518
+ // TODO: implement your filter-items logic here
519
+
520
+ return {
521
+ items: [],
522
+ };
523
+ }
524
+
525
+ groupItems(
526
+ _request: Billing.RecurringBillingItemHandling.GroupItemsInput,
527
+ _config: MyRecurringBillingItemHandlingConfig,
528
+ _context: Context
529
+ ) {
530
+ // TODO: implement your group-items logic here
531
+
532
+ return {
533
+ groups: [],
534
+ };
535
+ }
536
+ }
537
+ `
538
+ },
539
+ {
540
+ path: "extensions/common/.prettierignore",
541
+ content: `dist
542
+ generated
543
+ node_modules
544
+ `
545
+ },
546
+ {
547
+ path: "extensions/common/eslint.config.mts.mustache",
548
+ content: `import eslint from '@eslint/js';
549
+ import { defineConfig } from 'eslint/config';
550
+ import tseslint from 'typescript-eslint';
551
+ import eslintConfigPrettier from 'eslint-config-prettier/flat';
552
+
553
+ import globals from 'globals';
554
+
555
+ import stripeAppsConfig from '@stripe/extensibility-eslint-plugin';
556
+ import extensionTypeConfig from '@stripe/extensibility-eslint-plugin/{{extensionInterfaceId}}';
557
+
558
+ export default defineConfig([
559
+ eslint.configs.recommended,
560
+ ...tseslint.configs.recommended,
561
+ ...stripeAppsConfig,
562
+ ...extensionTypeConfig,
563
+
564
+ // Global ignores
565
+ {
566
+ ignores: ['dist', 'generated', 'node_modules'],
567
+ },
568
+
569
+ // TypeScript source files (with type-checking)
570
+ {
571
+ name: 'sources',
572
+ files: ['src/**/*.ts'],
573
+ ignores: ['**/*.test.ts', '**/__tests__/**'],
574
+ languageOptions: {
575
+ globals: {
576
+ ...globals.node,
577
+ },
578
+ parserOptions: {
579
+ projectService: true,
580
+ tsconfigRootDir: import.meta.dirname,
581
+ },
582
+ },
583
+ },
584
+
585
+ // Test files (type-checking, relaxed rules)
586
+ {
587
+ name: 'tests',
588
+ files: ['src/**/*.test.ts', 'src/**/__tests__/**/*.ts'],
589
+ languageOptions: {
590
+ globals: {
591
+ ...globals.node,
592
+ },
593
+ parserOptions: {
594
+ projectService: true,
595
+ tsconfigRootDir: import.meta.dirname,
596
+ },
597
+ },
598
+ rules: {
599
+ '@typescript-eslint/no-explicit-any': 'off',
600
+ '@typescript-eslint/no-unused-vars': 'off',
601
+ 'no-console': 'off',
602
+ },
603
+ },
604
+
605
+ // Config files
606
+ {
607
+ name: 'ts-configs',
608
+ files: ['*.config.m?ts', 'eslint.config.mts'],
609
+ languageOptions: {
610
+ globals: {
611
+ ...globals.node,
612
+ },
613
+ parserOptions: {
614
+ projectService: false,
615
+ },
616
+ },
617
+ rules: {
618
+ '@typescript-eslint/no-unused-vars': 'off',
619
+ },
620
+ },
621
+
622
+ // JavaScript/MJS files (scripts, configs)
623
+ {
624
+ name: 'js-configs',
625
+ files: ['**/*.js', '**/*.mjs'],
626
+ languageOptions: {
627
+ globals: {
628
+ ...globals.node,
629
+ },
630
+ parserOptions: {
631
+ projectService: false,
632
+ },
633
+ },
634
+ rules: {
635
+ '@typescript-eslint/no-require-imports': 'off',
636
+ '@typescript-eslint/no-unused-vars': 'off',
637
+ 'no-unused-vars': 'off',
638
+ },
639
+ },
640
+
641
+ eslintConfigPrettier,
642
+ ]);
643
+ `
644
+ },
645
+ {
646
+ path: "extensions/common/package.json.mustache",
647
+ content: `{
648
+ "name": "{{id}}",
649
+ "version": "{{version}}",
650
+ "description": "{{name}}",
651
+ "private": true,
652
+ "license": "~~proprietary~~",
653
+ "type": "module",
654
+ "scripts": {
655
+ "build": "tsc -p tsconfig.build.json && pnpm build:schemas",
656
+ "build:schemas": "gen-schemas --root ../.. --extension-id \\"$npm_package_name\\" --out-dir generated --schema-name config",
657
+ "lint": "pnpm lint:types && pnpm lint:eslint && pnpm lint:format",
658
+ "lint:types": "tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.json --noEmit",
659
+ "lint:eslint": "eslint .",
660
+ "lint:format": "prettier --check .",
661
+ "fix:lint": "eslint --fix .",
662
+ "fix:format": "prettier --write .",
663
+ "test": "vitest --root ../.. run --project \\"$npm_package_name\\"",
664
+ "test:watch": "pnpm test --watch",
665
+ "dev": "concurrently -n build,lint,test -c blue,yellow,green 'tsc -p tsconfig.json -w --noEmit' 'chokidar \\"src/**/*.{ts,json}\\" -c \\"eslint .\\"' 'pnpm test --watch --no-clear-screen'",
666
+ "stripe:regen": "gen-workspace --root ../.. --package-dir . --template-id {{extensionInterfaceId}}"
667
+ },
668
+ "lint-staged": {
669
+ "*.{ts,mts,mjs,js}": ["eslint --fix", "prettier --write"],
670
+ "*.{json,md,yaml}": "prettier --write"
671
+ }
672
+ }
673
+ `
674
+ },
675
+ {
676
+ path: "extensions/common/tsconfig.build.json.mustache",
677
+ content: `{
678
+ "extends": "../../tsconfig.base.json",
679
+ "compilerOptions": {
680
+ "lib": null,
681
+ "noLib": true,
682
+ "outDir": "./dist",
683
+ "rootDir": "./src"
684
+ },
685
+ "include": [
686
+ "src/**/*.ts",
687
+ "node_modules/@stripe/extensibility-sdk/tslibs/5.9.3/lib.es2022.{{executionProfile}}.d.ts",
688
+ "node_modules/@stripe/extensibility-sdk/tslibs/lib.{{executionProfile}}.globals.d.ts"
689
+ ],
690
+ "exclude": ["src/**/*.test.ts", "src/**/__tests__"]
691
+ }
692
+ `
693
+ },
694
+ {
695
+ path: "extensions/common/tsconfig.json.mustache",
696
+ content: `{
697
+ "extends": "../../tsconfig.json",
698
+ "compilerOptions": {
699
+ "rootDir": ".",
700
+ "plugins": [
701
+ {
702
+ "name": "@stripe/extensibility-language-server/plugin"
703
+ }
704
+ ]
705
+ },
706
+ "include": [
707
+ "src/**/*.ts",
708
+ "node_modules/@stripe/extensibility-sdk/tslibs/lib.{{executionProfile}}.globals.d.ts"
709
+ ],
710
+ "exclude": ["dist"]
711
+ }
712
+ `
713
+ },
714
+ {
715
+ path: "extensions/core.workflows.custom_action/custom_input.schema.json",
716
+ content: `{
717
+ "$schema": "http://json-schema.org/draft-07/schema#",
718
+ "type": "object",
719
+ "properties": {},
720
+ "additionalProperties": false
721
+ }
722
+ `
723
+ },
724
+ {
725
+ path: "extensions/core.workflows.custom_action/index.test.ts",
726
+ content: `import { beforeEach, describe, it, expect } from 'vitest';
727
+
728
+ import MyCustomAction from './index.js';
729
+
730
+ describe('MyCustomAction', () => {
731
+ let instance: MyCustomAction;
732
+
733
+ beforeEach(() => {
734
+ instance = new MyCustomAction();
735
+ });
736
+
737
+ it('should be constructable', () => {
738
+ expect(instance).toBeInstanceOf(MyCustomAction);
739
+ });
740
+ });
741
+ `
742
+ },
743
+ {
744
+ path: "extensions/core.workflows.custom_action/index.ts",
745
+ content: `import type { Core } from '@stripe/extensibility-sdk/extensions';
746
+ import type { Context } from '@stripe/extensibility-sdk/extensions';
747
+
748
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
749
+ interface MyCustomActionConfig extends Record<string, unknown> {}
750
+
751
+ export default class MyCustomAction implements Core.Workflows
752
+ .CustomAction<MyCustomActionConfig> {
753
+ execute(
754
+ _request: Core.Workflows.CustomAction.ExecuteCustomActionRequest,
755
+ _config: MyCustomActionConfig,
756
+ _context: Context
757
+ ) {
758
+ // TODO: implement your action logic here
759
+
760
+ return {};
761
+ }
762
+
763
+ getFormState(
764
+ _request: Core.Workflows.CustomAction.GetFormStateRequest,
765
+ _config: MyCustomActionConfig,
766
+ _context: Context
767
+ ) {
768
+ // TODO: implement your logic here
769
+
770
+ return {
771
+ values: {},
772
+ config: {},
773
+ };
774
+ }
775
+ }
776
+ `
777
+ },
778
+ {
779
+ path: "extensions/extend.workflows.custom_action/custom_input.schema.json",
780
+ content: `{
781
+ "$schema": "http://json-schema.org/draft-07/schema#",
782
+ "type": "object",
783
+ "properties": {},
784
+ "additionalProperties": false
785
+ }
786
+ `
787
+ },
788
+ {
789
+ path: "extensions/extend.workflows.custom_action/index.test.ts",
790
+ content: `import { beforeEach, describe, it, expect } from 'vitest';
791
+
792
+ import MyCustomAction from './index.js';
793
+
794
+ describe('MyCustomAction', () => {
795
+ let instance: MyCustomAction;
796
+
797
+ beforeEach(() => {
798
+ instance = new MyCustomAction();
799
+ });
800
+
801
+ it('should be constructable', () => {
802
+ expect(instance).toBeInstanceOf(MyCustomAction);
803
+ });
804
+ });
805
+ `
806
+ },
807
+ {
808
+ path: "extensions/extend.workflows.custom_action/index.ts",
809
+ content: `import type { Extend } from '@stripe/extensibility-sdk/extensions';
810
+ import type { Context } from '@stripe/extensibility-sdk/extensions';
811
+
812
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
813
+ interface MyCustomActionConfig extends Record<string, unknown> {}
814
+
815
+ export default class MyCustomAction implements Extend.Workflows
816
+ .CustomAction<MyCustomActionConfig> {
817
+ execute(
818
+ _request: Extend.Workflows.CustomAction.ExecuteCustomActionRequest,
819
+ _config: MyCustomActionConfig,
820
+ _context: Context
821
+ ) {
822
+ // TODO: implement your action logic here
823
+
824
+ return {};
825
+ }
826
+
827
+ getFormState(
828
+ _request: Extend.Workflows.CustomAction.GetFormStateRequest,
829
+ _config: MyCustomActionConfig,
830
+ _context: Context
831
+ ) {
832
+ // TODO: implement your logic here
833
+
834
+ return {
835
+ values: {},
836
+ config: {},
837
+ };
838
+ }
839
+ }
840
+ `
841
+ },
842
+ {
843
+ path: "root/.husky/pre-commit",
844
+ content: `pnpm lint-staged
845
+ `
846
+ },
847
+ {
848
+ path: "root/.prettierignore",
849
+ content: `dist
850
+ generated
851
+ node_modules
852
+ pnpm-lock.yaml
853
+ .build
854
+ `
855
+ },
856
+ {
857
+ path: "root/.prettierrc",
858
+ content: `{
859
+ "semi": true,
860
+ "singleQuote": true,
861
+ "tabWidth": 2,
862
+ "trailingComma": "es5",
863
+ "printWidth": 90
864
+ }
865
+ `
866
+ },
867
+ {
868
+ path: "root/_gitignore",
869
+ content: `# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
870
+
871
+ *~
872
+
873
+ # dependencies
874
+ node_modules
875
+ dist
876
+
877
+ # testing
878
+ /coverage
879
+
880
+ # production
881
+ /.build
882
+
883
+ # misc
884
+ .DS_Store
885
+ .env.local
886
+ .env.development.local
887
+ .env.test.local
888
+ .env.production.local
889
+
890
+ npm-debug.log*
891
+ yarn-debug.log*
892
+ yarn-error.log*
893
+ install-deps.log
894
+
895
+ # generated schemas
896
+ generated
897
+ `
898
+ },
899
+ {
900
+ path: "root/custom-objects/package.json",
901
+ content: `{
902
+ "name": "custom-objects",
903
+ "type": "module",
904
+ "version": "0.0.1",
905
+ "license": "UNLICENSED",
906
+ "private": true,
907
+ "scripts": {
908
+ "build": "test -d src && custom-objects-build --input src --output dist || true",
909
+ "lint:types": "test ! -d src || tsc --noEmit",
910
+ "test": "vitest run"
911
+ },
912
+ "dependencies": {
913
+ "@stripe/extensibility-custom-objects": "0.7.4",
914
+ "@stripe/extensibility-sdk": "0.22.4"
915
+ },
916
+ "devDependencies": {
917
+ "@stripe/extensibility-custom-objects-tools": "0.40.0",
918
+ "@stripe/extensibility-test-helpers": "0.2.7"
919
+ }
920
+ }
921
+ `
922
+ },
923
+ {
924
+ path: "root/custom-objects/tsconfig.json",
925
+ content: `{
926
+ "extends": "../tsconfig.base.json",
927
+ "compilerOptions": {
928
+ "module": "ESNext",
929
+ "moduleResolution": "bundler",
930
+ "types": ["vitest/globals"]
931
+ },
932
+ "exclude": ["dist"]
933
+ }
934
+ `
935
+ },
936
+ {
937
+ path: "root/eslint.config.mts",
938
+ content: `import { readFileSync } from 'node:fs';
939
+ import { dirname, resolve } from 'node:path';
940
+ import { fileURLToPath } from 'node:url';
941
+ import eslint from '@eslint/js';
942
+ import { defineConfig } from 'eslint/config';
943
+ import tseslint from 'typescript-eslint';
944
+ import workspaces from 'eslint-plugin-workspaces';
945
+ import eslintConfigPrettier from 'eslint-config-prettier/flat';
946
+
947
+ import globals from 'globals';
948
+
949
+ import stripeAppsConfig from '@stripe/extensibility-eslint-plugin';
950
+
951
+ // Read additional ignore globs from package.json (written by the generate plugin
952
+ // to exclude generated SDK directories from linting).
953
+ let stripeGlobsToIgnore: string[] = [];
954
+ try {
955
+ const configDir = dirname(fileURLToPath(import.meta.url));
956
+ const packageJsonPath = resolve(configDir, './package.json');
957
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
958
+ const globs = pkg?.stripe?.eslintIgnoreGlobs;
959
+ if (Array.isArray(globs)) {
960
+ stripeGlobsToIgnore = globs.filter(
961
+ (g: unknown): g is string => typeof g === 'string'
962
+ );
963
+ }
964
+ } catch {
965
+ // package.json not found or unparseable \u2014 ignore
966
+ }
967
+
968
+ export default defineConfig([
969
+ eslint.configs.recommended,
970
+ ...tseslint.configs.recommended,
971
+ ...stripeAppsConfig,
972
+
973
+ // Global ignores
974
+ {
975
+ ignores: [
976
+ '.build',
977
+ '**/dist',
978
+ '**/generated',
979
+ '**/node_modules',
980
+ 'extensions/**',
981
+ 'custom-objects',
982
+ 'ui',
983
+ ...stripeGlobsToIgnore,
984
+ ],
985
+ },
986
+
987
+ // Common rules for all files
988
+ {
989
+ plugins: { workspaces },
990
+ rules: {
991
+ ...workspaces.configs.recommended.rules,
992
+ },
993
+ },
994
+
995
+ // Config files (vitest.config.ts, eslint.config.ts, etc.)
996
+ {
997
+ name: 'ts-configs',
998
+ files: ['extensions/*/*.config.m?ts', '*.config.m?ts', 'eslint.config.mts'],
999
+ languageOptions: {
1000
+ globals: {
1001
+ ...globals.node,
1002
+ },
1003
+ parserOptions: {
1004
+ projectService: false,
1005
+ },
1006
+ },
1007
+ rules: {
1008
+ '@typescript-eslint/no-unused-vars': 'off',
1009
+ },
1010
+ },
1011
+
1012
+ // JavaScript/MJS files (scripts, configs)
1013
+ {
1014
+ name: 'js-configs',
1015
+ files: ['**/*.js', '**/*.mjs'],
1016
+ languageOptions: {
1017
+ globals: {
1018
+ ...globals.node,
1019
+ },
1020
+ parserOptions: {
1021
+ projectService: false,
1022
+ },
1023
+ },
1024
+ rules: {
1025
+ '@typescript-eslint/no-require-imports': 'off',
1026
+ '@typescript-eslint/no-unused-vars': 'off',
1027
+ 'no-unused-vars': 'off',
1028
+ },
1029
+ },
1030
+
1031
+ eslintConfigPrettier,
1032
+ ]);
1033
+ `
1034
+ },
1035
+ {
1036
+ path: "root/package.json.mustache",
1037
+ content: `{
1038
+ "name": "{{ appId }}",
1039
+ "version": "{{ version }}",
1040
+ "description": "{{ appName }}",
1041
+ "private": true,
1042
+ "license": "~~proprietary~~",
1043
+ "engines": {
1044
+ "node": ">=20.0.0"
1045
+ },
1046
+ "packageManager": "pnpm@10.30.3",
1047
+ "scripts": {
1048
+ "build": "pnpm -r --if-present build",
1049
+ "lint": "pnpm lint:types && pnpm lint:eslint && pnpm lint:format",
1050
+ "lint:types": "pnpm -r --if-present lint:types",
1051
+ "lint:eslint": "eslint . --ignore-pattern 'extensions/**' && pnpm -r --filter './extensions/*' --if-present lint:eslint",
1052
+ "lint:format": "prettier --check .",
1053
+ "fix:lint": "eslint --fix . --ignore-pattern 'extensions/**' && pnpm -r --filter './extensions/*' --if-present fix:lint",
1054
+ "fix:format": "prettier --write .",
1055
+ "test": "tsx tools/test.mts",
1056
+ "test:watch": "vitest watch",
1057
+ "check": "pnpm build && pnpm lint && pnpm test",
1058
+ "prepare": "husky",
1059
+ "preimage": "pnpm install --lockfile-only && pnpm build && pnpm lint:types && pnpm lint:eslint && pnpm test",
1060
+ "image": "rm -rf .build && create-upload-image .build",
1061
+ "postimage": "cp pnpm-lock.yaml .build/",
1062
+ "stripe:regen": "gen-workspace --package-dir ."
1063
+ },
1064
+ "lint-staged": {
1065
+ "*.{ts,mts,mjs,js}": ["eslint --fix", "prettier --write"],
1066
+ "*.{json,md,yaml}": "prettier --write"
1067
+ }
1068
+ }
1069
+ `
1070
+ },
1071
+ {
1072
+ path: "root/pnpm-workspace.yaml",
1073
+ content: `packages:
1074
+ - extensions/*
1075
+ - custom-objects
1076
+ - ui
1077
+
1078
+ overrides:
1079
+ vite: ^6.0.0
1080
+ `
1081
+ },
1082
+ {
1083
+ path: "root/stripe-app.yaml.mustache",
1084
+ content: `$schema: https://stripe.com/stripe-app/v2.0.0/schema
1085
+ id: '{{ appId }}'
1086
+ name: '{{ appName }}'
1087
+ version: 0.0.1
1088
+ declarations:
1089
+ distribution_type: private
1090
+ `
1091
+ },
1092
+ {
1093
+ path: "root/tools/test.mts",
1094
+ content: `#!/usr/bin/env tsx
1095
+ /**
1096
+ * Runs tests across the workspace:
1097
+ * - vitest for script extensions and custom objects (extensions/*)
1098
+ * - jest for UI extensions (ui/)
1099
+ */
1100
+ import { existsSync, readdirSync } from 'node:fs';
1101
+ import { execSync } from 'node:child_process';
1102
+
1103
+ const hasExtensions =
1104
+ existsSync('extensions') &&
1105
+ readdirSync('extensions').some((name) => existsSync(\`extensions/\${name}/package.json\`));
1106
+
1107
+ const hasUI = existsSync('ui/package.json');
1108
+
1109
+ let exitCode = 0;
1110
+
1111
+ function run(cmd: string): void {
1112
+ try {
1113
+ execSync(cmd, { stdio: 'inherit' });
1114
+ } catch (e: unknown) {
1115
+ exitCode = (e as NodeJS.ErrnoException & { status?: number }).status ?? 1;
1116
+ }
1117
+ }
1118
+
1119
+ if (hasExtensions) {
1120
+ run('vitest run');
1121
+ }
1122
+
1123
+ if (hasUI) {
1124
+ try {
1125
+ execSync('pnpm --filter "./ui" test', { stdio: 'inherit' });
1126
+ } catch {
1127
+ // UI test failures are non-fatal
1128
+ }
1129
+ }
1130
+
1131
+ process.exit(exitCode);
1132
+ `
1133
+ },
1134
+ {
1135
+ path: "root/tsconfig.base.json",
1136
+ content: `{
1137
+ "compilerOptions": {
1138
+ "target": "ES2022",
1139
+ "module": "NodeNext",
1140
+ "moduleResolution": "NodeNext",
1141
+ "lib": ["ES2022"],
1142
+ "declaration": true,
1143
+ "declarationMap": true,
1144
+ "esModuleInterop": true,
1145
+ "exactOptionalPropertyTypes": false,
1146
+ "forceConsistentCasingInFileNames": true,
1147
+ "isolatedModules": true,
1148
+ "noFallthroughCasesInSwitch": true,
1149
+ "noImplicitReturns": true,
1150
+ "removeComments": false,
1151
+ "resolveJsonModule": true,
1152
+ "skipLibCheck": true,
1153
+ "sourceMap": true,
1154
+ "strict": true,
1155
+ "types": [],
1156
+ "verbatimModuleSyntax": true
1157
+ }
1158
+ }
1159
+ `
1160
+ },
1161
+ {
1162
+ path: "root/tsconfig.json",
1163
+ content: `{
1164
+ "extends": "./tsconfig.base.json",
1165
+ "compilerOptions": {
1166
+ "noEmit": true,
1167
+ "plugins": [
1168
+ {
1169
+ "name": "@stripe/extensibility-language-server/plugin"
1170
+ }
1171
+ ],
1172
+ "preserveWatchOutput": true,
1173
+ "types": ["vitest/globals", "node"]
1174
+ },
1175
+ "include": ["**/*.ts"],
1176
+ "exclude": ["**/node_modules", "**/dist"]
1177
+ }
1178
+ `
1179
+ },
1180
+ {
1181
+ path: "root/ui/package.json",
1182
+ content: `{
1183
+ "name": "ui",
1184
+ "version": "0.0.1",
1185
+ "license": "UNLICENSED",
1186
+ "private": true,
1187
+ "scripts": {
1188
+ "test": "jest --passWithNoTests"
1189
+ },
1190
+ "dependencies": {
1191
+ "@stripe/ui-extension-sdk": "^9.1.0"
1192
+ },
1193
+ "devDependencies": {
1194
+ "@stripe/ui-extension-tools": "^0.0.1",
1195
+ "@types/jest": "^27.5.2",
1196
+ "@types/react": "^17.0.2"
1197
+ }
1198
+ }
1199
+ `
1200
+ },
1201
+ {
1202
+ path: "root/vitest.config.mts",
1203
+ content: `import { defineConfig } from 'vitest/config';
1204
+ import { existsSync, readdirSync } from 'fs';
1205
+ import { dirname, resolve } from 'path';
1206
+ import { fileURLToPath } from 'url';
1207
+
1208
+ const rootDir = dirname(fileURLToPath(import.meta.url));
1209
+ const extensionsDir = resolve(rootDir, 'extensions');
1210
+
1211
+ const extensionProjects = existsSync(extensionsDir)
1212
+ ? readdirSync(extensionsDir)
1213
+ .filter((name) => existsSync(resolve(extensionsDir, name, 'package.json')))
1214
+ .map((name) => resolve(extensionsDir, name))
1215
+ : [];
1216
+
1217
+ const coProjects = existsSync(resolve(rootDir, 'custom-objects/package.json'))
1218
+ ? [resolve(rootDir, 'custom-objects')]
1219
+ : [];
1220
+
1221
+ const projects = [...extensionProjects, ...coProjects];
1222
+
1223
+ if (projects.length === 0) {
1224
+ console.debug(\`No vitest projects detected. This means either:
1225
+ - You have no extension projects defined, in which case this warning is expected.
1226
+ - There is an internal error detecting vitest projects relative to the project root
1227
+ \${rootDir}.
1228
+ \`);
1229
+ }
1230
+
1231
+ export default projects.length > 0
1232
+ ? defineConfig({
1233
+ test: {
1234
+ projects,
1235
+ passWithNoTests: true,
1236
+
1237
+ // Only run tests from src, not compiled dist
1238
+ exclude: ['**/node_modules', '**/dist'],
1239
+ // Place snapshots alongside test files instead of in __snapshots__
1240
+ snapshotFormat: {
1241
+ escapeString: false,
1242
+ printBasicPrototype: false,
1243
+ },
1244
+ resolveSnapshotPath: (testPath, snapExtension) => {
1245
+ return testPath.replace(/\\.test\\.ts$/, \`.test\${snapExtension}\`);
1246
+ },
1247
+ },
1248
+ })
1249
+ : defineConfig({ test: { passWithNoTests: true, include: [] } });
1250
+ `
1251
+ }
1252
+ ];
1253
+ var _fs = (0, import_extensibility_tool_utils4._createInMemoryTemplateFS)(TEMPLATE_FS_IMAGE);
1254
+
1255
+ // src/dependencies/index.ts
1256
+ var import_node_path = __toESM(require("path"), 1);
1257
+ var import_node_fs = __toESM(require("fs"), 1);
1258
+ var import_node_os = __toESM(require("os"), 1);
1259
+ var import_node_child_process = require("child_process");
1260
+ var import_package_json = __toESM(require("@npmcli/package-json"), 1);
1261
+ var semver = __toESM(require("semver"), 1);
1262
+ var import_js_toml = require("js-toml");
1263
+ var import_extensibility_tool_utils5 = require("@stripe/extensibility-tool-utils");
1264
+ function _npmDep(name, version) {
1265
+ return { type: "npm", name, version };
1266
+ }
1267
+ function _devNpmDep(name, version) {
1268
+ return { type: "dev-npm", name, version };
1269
+ }
1270
+
1271
+ // src/templates/extensions/base.ts
1272
+ var import_extensibility_tool_utils6 = require("@stripe/extensibility-tool-utils");
1273
+ var SDK_PACKAGE_NAME = "@stripe/extensibility-sdk";
1274
+ var LANGUAGE_SERVER_PACKAGE_NAME = "@stripe/extensibility-language-server";
1275
+ var LANGUAGE_SERVER_PACKAGE_VERSION = "^0.2.0";
1276
+ function _createExtensionEslintConfigFile(params, context) {
1277
+ const { id, extensionInterfaceId } = params;
1278
+ const { fs: fs4 } = context;
1279
+ return {
1280
+ path: `extensions/${id}/eslint.config.mts`,
1281
+ content: fs4.mustache(
1282
+ { extensionInterfaceId },
1283
+ "common",
1284
+ "eslint.config.mts.mustache"
1285
+ ),
1286
+ precious: true
1287
+ };
1288
+ }
1289
+ function _createBaseOutput(params, context) {
1290
+ const { id } = params;
1291
+ const { fs: fs4 } = context;
1292
+ return {
1293
+ files: [
1294
+ {
1295
+ path: `extensions/${id}/package.json`,
1296
+ content: fs4.mustache(params, "common", "package.json.mustache")
1297
+ },
1298
+ {
1299
+ ..._createExtensionEslintConfigFile(params, context)
1300
+ },
1301
+ {
1302
+ path: `extensions/${id}/tsconfig.build.json`,
1303
+ content: fs4.mustache(params, "common", "tsconfig.build.json.mustache")
1304
+ },
1305
+ {
1306
+ path: `extensions/${id}/tsconfig.json`,
1307
+ content: fs4.mustache(params, "common", "tsconfig.json.mustache")
1308
+ },
1309
+ {
1310
+ path: `extensions/${id}/.prettierignore`,
1311
+ content: fs4.textFile("common", ".prettierignore")
1312
+ }
1313
+ ],
1314
+ methods: {},
1315
+ dependencies: {
1316
+ // Exact pin (no caret) — the SDK is tightly coupled to dev-tools and
1317
+ // must match the version that generated the extension scaffolding.
1318
+ runtime: [_npmDep(SDK_PACKAGE_NAME, (0, import_extensibility_tool_utils6._workspaceVersion)(SDK_PACKAGE_NAME))],
1319
+ dev: [_devNpmDep(LANGUAGE_SERVER_PACKAGE_NAME, LANGUAGE_SERVER_PACKAGE_VERSION)]
1320
+ },
1321
+ postGenerationHooks: [
1322
+ {
1323
+ script: "build",
1324
+ description: "Build extension"
1325
+ },
1326
+ {
1327
+ exec: "prettier --write .",
1328
+ description: "Format generated files"
1329
+ }
1330
+ ]
1331
+ };
1332
+ }
1333
+
1334
+ // src/templates/extensions/core.workflows.custom_action.ts
1335
+ var EXTENSION_INTERFACE_ID = "core.workflows.custom_action";
1336
+ var customActionTemplate = {
1337
+ methods: {
1338
+ execute: { implementation_types: ["script", "remote-function"] },
1339
+ get_form_state: { implementation_types: ["script", "remote-function"] }
1340
+ },
1341
+ description: "Custom actions let your app define actions that users can add to their automated workflows. When a workflow triggers a custom action, Stripe calls your app to execute it.",
1342
+ generate: (params, context) => {
1343
+ const { id } = params;
1344
+ const { fs: fs4 } = context;
1345
+ const base = _createBaseOutput(
1346
+ {
1347
+ ...params,
1348
+ extensionInterfaceId: EXTENSION_INTERFACE_ID,
1349
+ executionProfile: "egress"
1350
+ },
1351
+ context
1352
+ );
1353
+ return {
1354
+ ...base,
1355
+ files: [
1356
+ {
1357
+ path: `extensions/${id}/src/index.ts`,
1358
+ content: fs4.textFile(EXTENSION_INTERFACE_ID, "index.ts"),
1359
+ precious: true
1360
+ },
1361
+ {
1362
+ path: `extensions/${id}/src/custom_input.schema.json`,
1363
+ content: fs4.textFile(EXTENSION_INTERFACE_ID, "custom_input.schema.json"),
1364
+ precious: true
1365
+ },
1366
+ {
1367
+ path: `extensions/${id}/src/index.test.ts`,
1368
+ content: fs4.textFile(EXTENSION_INTERFACE_ID, "index.test.ts"),
1369
+ precious: true
1370
+ },
1371
+ ...base.files
1372
+ ],
1373
+ methods: {
1374
+ execute: {
1375
+ implementation_type: "script",
1376
+ custom_input: {
1377
+ input_schema: {
1378
+ type: "json_schema",
1379
+ content: `extensions/${id}/src/custom_input.schema.json`
1380
+ }
1381
+ }
1382
+ },
1383
+ get_form_state: {
1384
+ implementation_type: "script"
1385
+ }
1386
+ }
1387
+ };
1388
+ }
1389
+ };
1390
+ var core_workflows_custom_action_default = {
1391
+ [EXTENSION_INTERFACE_ID]: customActionTemplate
1392
+ };
1393
+
1394
+ // src/templates/extensions/extend.objects.custom_objects.ts
1395
+ var EXTENSION_INTERFACE_ID2 = "extend.objects.custom_objects";
1396
+ var customObjectsTemplate = {
1397
+ methods: {
1398
+ execute_method: { implementation_types: ["script"] }
1399
+ },
1400
+ description: "Methods extension for custom object actions. Generated at build time \u2014 the transformed custom object class becomes the entry point.",
1401
+ hidden: true,
1402
+ generate: (params, context) => {
1403
+ const base = _createBaseOutput(
1404
+ {
1405
+ ...params,
1406
+ extensionInterfaceId: EXTENSION_INTERFACE_ID2,
1407
+ executionProfile: "egress"
1408
+ },
1409
+ context
1410
+ );
1411
+ return {
1412
+ ...base,
1413
+ methods: {
1414
+ execute_method: {
1415
+ implementation_type: "script"
1416
+ }
1417
+ }
1418
+ };
1419
+ }
1420
+ };
1421
+ var extend_objects_custom_objects_default = {
1422
+ [EXTENSION_INTERFACE_ID2]: customObjectsTemplate
1423
+ };
1424
+
1425
+ // src/templates/extensions/extend.workflows.custom_action.ts
1426
+ var EXTENSION_INTERFACE_ID3 = "extend.workflows.custom_action";
1427
+ var extendCustomActionTemplate = {
1428
+ methods: {
1429
+ execute: { implementation_types: ["script", "remote-function"] },
1430
+ get_form_state: { implementation_types: ["script", "remote-function"] }
1431
+ },
1432
+ description: "Custom actions let your app define actions that users can add to their automated workflows. When a workflow triggers a custom action, Stripe calls your app to execute it.",
1433
+ generate: (params, context) => {
1434
+ const { id } = params;
1435
+ const { fs: fs4 } = context;
1436
+ const base = _createBaseOutput(
1437
+ {
1438
+ ...params,
1439
+ extensionInterfaceId: EXTENSION_INTERFACE_ID3,
1440
+ executionProfile: "egress"
1441
+ },
1442
+ context
1443
+ );
1444
+ return {
1445
+ ...base,
1446
+ files: [
1447
+ {
1448
+ path: `extensions/${id}/src/index.ts`,
1449
+ content: fs4.textFile(EXTENSION_INTERFACE_ID3, "index.ts"),
1450
+ precious: true
1451
+ },
1452
+ {
1453
+ path: `extensions/${id}/src/custom_input.schema.json`,
1454
+ content: fs4.textFile(EXTENSION_INTERFACE_ID3, "custom_input.schema.json"),
1455
+ precious: true
1456
+ },
1457
+ {
1458
+ path: `extensions/${id}/src/index.test.ts`,
1459
+ content: fs4.textFile(EXTENSION_INTERFACE_ID3, "index.test.ts"),
1460
+ precious: true
1461
+ },
1462
+ ...base.files
1463
+ ],
1464
+ methods: {
1465
+ execute: {
1466
+ implementation_type: "script",
1467
+ custom_input: {
1468
+ input_schema: {
1469
+ type: "json_schema",
1470
+ content: `extensions/${id}/src/custom_input.schema.json`
1471
+ }
1472
+ }
1473
+ },
1474
+ get_form_state: {
1475
+ implementation_type: "script"
1476
+ }
1477
+ }
1478
+ };
1479
+ }
1480
+ };
1481
+ var extend_workflows_custom_action_default = {
1482
+ [EXTENSION_INTERFACE_ID3]: extendCustomActionTemplate
1483
+ };
1484
+
1485
+ // src/templates/extensions/billing.customer_balance_application.ts
1486
+ var EXTENSION_INTERFACE_ID4 = "billing.customer_balance_application";
1487
+ var customerBalanceApplicationTemplate = {
1488
+ methods: {
1489
+ compute_applied_customer_balance: { implementation_types: ["script"] }
1490
+ },
1491
+ description: "Implement custom logic to control when and how much customer balance is applied to subscription invoices using scripts.",
1492
+ generate: (params, context) => {
1493
+ const { id } = params;
1494
+ const { fs: fs4 } = context;
1495
+ const base = _createBaseOutput(
1496
+ {
1497
+ ...params,
1498
+ extensionInterfaceId: EXTENSION_INTERFACE_ID4,
1499
+ executionProfile: "restricted"
1500
+ },
1501
+ context
1502
+ );
1503
+ return {
1504
+ ...base,
1505
+ files: [
1506
+ {
1507
+ path: `extensions/${id}/src/index.ts`,
1508
+ content: fs4.textFile(EXTENSION_INTERFACE_ID4, "index.ts"),
1509
+ precious: true
1510
+ },
1511
+ {
1512
+ path: `extensions/${id}/src/index.test.ts`,
1513
+ content: fs4.textFile(EXTENSION_INTERFACE_ID4, "index.test.ts"),
1514
+ precious: true
1515
+ },
1516
+ ...base.files
1517
+ ],
1518
+ methods: {
1519
+ compute_applied_customer_balance: {
1520
+ implementation_type: "script"
1521
+ }
1522
+ }
1523
+ };
1524
+ }
1525
+ };
1526
+ var billing_customer_balance_application_default = {
1527
+ [EXTENSION_INTERFACE_ID4]: customerBalanceApplicationTemplate
1528
+ };
1529
+
1530
+ // src/templates/extensions/billing.bill.discount_calculation.ts
1531
+ var EXTENSION_INTERFACE_ID5 = "billing.bill.discount_calculation";
1532
+ var discountCalculationTemplate = {
1533
+ hidden: true,
1534
+ methods: {
1535
+ compute_discounts: { implementation_types: ["script"] }
1536
+ },
1537
+ description: "Create coupons with custom scripting logic for dynamic discounts based on subscription attributes, customer metadata, and complex business rules.",
1538
+ generate: (params, context) => {
1539
+ const { id } = params;
1540
+ const { fs: fs4 } = context;
1541
+ const base = _createBaseOutput(
1542
+ {
1543
+ ...params,
1544
+ extensionInterfaceId: EXTENSION_INTERFACE_ID5,
1545
+ executionProfile: "restricted"
1546
+ },
1547
+ context
1548
+ );
1549
+ return {
1550
+ ...base,
1551
+ files: [
1552
+ {
1553
+ path: `extensions/${id}/src/index.ts`,
1554
+ content: fs4.textFile(EXTENSION_INTERFACE_ID5, "index.ts"),
1555
+ precious: true
1556
+ },
1557
+ {
1558
+ path: `extensions/${id}/src/index.test.ts`,
1559
+ content: fs4.textFile(EXTENSION_INTERFACE_ID5, "index.test.ts"),
1560
+ precious: true
1561
+ },
1562
+ ...base.files
1563
+ ],
1564
+ methods: {
1565
+ compute_discounts: {
1566
+ implementation_type: "script"
1567
+ }
1568
+ }
1569
+ };
1570
+ }
1571
+ };
1572
+ var billing_bill_discount_calculation_default = {
1573
+ [EXTENSION_INTERFACE_ID5]: discountCalculationTemplate
1574
+ };
1575
+
1576
+ // src/templates/extensions/billing.invoice_collection_setting.ts
1577
+ var EXTENSION_INTERFACE_ID6 = "billing.invoice_collection_setting";
1578
+ var invoiceCollectionSettingTemplate = {
1579
+ hidden: true,
1580
+ methods: {
1581
+ collection_override: { implementation_types: ["script"] }
1582
+ },
1583
+ description: "Use Stripe Scripts to create custom invoice collection logic that controls how your integration handles invoices generated from subscriptions.",
1584
+ generate: (params, context) => {
1585
+ const { id } = params;
1586
+ const { fs: fs4 } = context;
1587
+ const base = _createBaseOutput(
1588
+ {
1589
+ ...params,
1590
+ extensionInterfaceId: EXTENSION_INTERFACE_ID6,
1591
+ executionProfile: "restricted"
1592
+ },
1593
+ context
1594
+ );
1595
+ return {
1596
+ ...base,
1597
+ files: [
1598
+ {
1599
+ path: `extensions/${id}/src/index.ts`,
1600
+ content: fs4.textFile(EXTENSION_INTERFACE_ID6, "index.ts"),
1601
+ precious: true
1602
+ },
1603
+ {
1604
+ path: `extensions/${id}/src/index.test.ts`,
1605
+ content: fs4.textFile(EXTENSION_INTERFACE_ID6, "index.test.ts"),
1606
+ precious: true
1607
+ },
1608
+ ...base.files
1609
+ ],
1610
+ methods: {
1611
+ collection_override: {
1612
+ implementation_type: "script"
1613
+ }
1614
+ }
1615
+ };
1616
+ }
1617
+ };
1618
+ var billing_invoice_collection_setting_default = {
1619
+ [EXTENSION_INTERFACE_ID6]: invoiceCollectionSettingTemplate
1620
+ };
1621
+
1622
+ // src/templates/extensions/billing.prorations.ts
1623
+ var EXTENSION_INTERFACE_ID7 = "billing.prorations";
1624
+ var prorationsTemplate = {
1625
+ methods: {
1626
+ prorate_items: { implementation_types: ["script"] }
1627
+ },
1628
+ description: "Create custom proration logic for subscriptions using scripts to handle upgrades, downgrades, and mid-cycle changes.",
1629
+ generate: (params, context) => {
1630
+ const { id } = params;
1631
+ const { fs: fs4 } = context;
1632
+ const base = _createBaseOutput(
1633
+ {
1634
+ ...params,
1635
+ extensionInterfaceId: EXTENSION_INTERFACE_ID7,
1636
+ executionProfile: "restricted"
1637
+ },
1638
+ context
1639
+ );
1640
+ return {
1641
+ ...base,
1642
+ files: [
1643
+ {
1644
+ path: `extensions/${id}/src/index.ts`,
1645
+ content: fs4.textFile(EXTENSION_INTERFACE_ID7, "index.ts"),
1646
+ precious: true
1647
+ },
1648
+ {
1649
+ path: `extensions/${id}/src/index.test.ts`,
1650
+ content: fs4.textFile(EXTENSION_INTERFACE_ID7, "index.test.ts"),
1651
+ precious: true
1652
+ },
1653
+ ...base.files
1654
+ ],
1655
+ methods: {
1656
+ prorate_items: {
1657
+ implementation_type: "script"
1658
+ }
1659
+ }
1660
+ };
1661
+ }
1662
+ };
1663
+ var billing_prorations_default = {
1664
+ [EXTENSION_INTERFACE_ID7]: prorationsTemplate
1665
+ };
1666
+
1667
+ // src/templates/extensions/billing.recurring_billing_item_handling.ts
1668
+ var EXTENSION_INTERFACE_ID8 = "billing.recurring_billing_item_handling";
1669
+ var template = {
1670
+ methods: {
1671
+ before_item_creation: { implementation_types: ["script"] },
1672
+ filter_items: { implementation_types: ["script"] },
1673
+ group_items: { implementation_types: ["script"] }
1674
+ },
1675
+ description: "Customize how recurring billing items are filtered, grouped, and created during subscription billing runs.",
1676
+ generate: (params, context) => {
1677
+ const { id } = params;
1678
+ const { fs: fs4 } = context;
1679
+ const base = _createBaseOutput(
1680
+ {
1681
+ ...params,
1682
+ extensionInterfaceId: EXTENSION_INTERFACE_ID8,
1683
+ executionProfile: "restricted"
1684
+ },
1685
+ context
1686
+ );
1687
+ return {
1688
+ ...base,
1689
+ files: [
1690
+ {
1691
+ path: `extensions/${id}/src/index.ts`,
1692
+ content: fs4.textFile(EXTENSION_INTERFACE_ID8, "index.ts"),
1693
+ precious: true
1694
+ },
1695
+ {
1696
+ path: `extensions/${id}/src/index.test.ts`,
1697
+ content: fs4.textFile(EXTENSION_INTERFACE_ID8, "index.test.ts"),
1698
+ precious: true
1699
+ },
1700
+ ...base.files
1701
+ ],
1702
+ methods: {
1703
+ before_item_creation: {
1704
+ implementation_type: "script"
1705
+ },
1706
+ filter_items: {
1707
+ implementation_type: "script"
1708
+ },
1709
+ group_items: {
1710
+ implementation_type: "script"
1711
+ }
1712
+ }
1713
+ };
1714
+ }
1715
+ };
1716
+ var billing_recurring_billing_item_handling_default = {
1717
+ [EXTENSION_INTERFACE_ID8]: template
1718
+ };
1719
+
1720
+ // src/templates/extensions/registry.ts
1721
+ var DEFAULT_TEMPLATES = {
1722
+ ...core_workflows_custom_action_default,
1723
+ ...extend_objects_custom_objects_default,
1724
+ ...extend_workflows_custom_action_default,
1725
+ ...billing_customer_balance_application_default,
1726
+ ...billing_bill_discount_calculation_default,
1727
+ ...billing_invoice_collection_setting_default,
1728
+ ...billing_prorations_default,
1729
+ ...billing_recurring_billing_item_handling_default
1730
+ };
1731
+ var registry_default = DEFAULT_TEMPLATES;
1732
+
1733
+ // src/templates/extensions/index.ts
1734
+ var _templateManager = new _ExtensionTemplateManager(
1735
+ registry_default,
1736
+ _fs.scope("extensions")
1737
+ );
1738
+
1739
+ // src/custom-objects/generated/proto/vendor/publicapi/v2ext.pb.ts
1740
+ var FieldPresence = {
1741
+ /** FIELD_PRESENCE_INVALID - buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX */
1742
+ FIELD_PRESENCE_INVALID: "FIELD_PRESENCE_INVALID",
1743
+ PRESENT: "PRESENT",
1744
+ NOT_PRESENT: "NOT_PRESENT",
1745
+ PRESENT_BUT_NULL: "PRESENT_BUT_NULL",
1746
+ UNRECOGNIZED: "UNRECOGNIZED"
1747
+ };
1748
+
1749
+ // src/custom-objects/generated/proto/custom_objects/pub/api/common/schema.pb.ts
1750
+ var DataType = {
1751
+ UNSPECIFIED: "UNSPECIFIED",
1752
+ STRING_TYPE: "STRING_TYPE",
1753
+ INTEGER_TYPE: "INTEGER_TYPE",
1754
+ BOOLEAN_TYPE: "BOOLEAN_TYPE",
1755
+ ENUM_TYPE: "ENUM_TYPE",
1756
+ OBJECT_REFERENCE_TYPE: "OBJECT_REFERENCE_TYPE",
1757
+ DATETIME_TYPE: "DATETIME_TYPE",
1758
+ UNRECOGNIZED: "UNRECOGNIZED"
1759
+ };
1760
+
1761
+ // src/custom-objects/to-proto-json.ts
1762
+ function toUpsertRequestJson(buildArtifact) {
1763
+ const { platformMetadata, fields, actions } = buildArtifact;
1764
+ return {
1765
+ apiNameSingular: platformMetadata.apiName,
1766
+ apiNamePlural: platformMetadata.apiNamePlural,
1767
+ displayNameSingular: platformMetadata.displayName,
1768
+ displayNamePlural: platformMetadata.displayNamePlural,
1769
+ description: fields.jsonSchema.description,
1770
+ primaryDisplayProperty: buildArtifact.primaryDisplayProperty ?? "",
1771
+ secondaryDisplayProperty: buildArtifact.secondaryDisplayProperty,
1772
+ properties: mapProperties(fields.jsonSchema),
1773
+ apiActions: mapActions(actions),
1774
+ metadata: void 0,
1775
+ targetCompartment: void 0
1776
+ };
1777
+ }
1778
+ function mapActions(actions) {
1779
+ const result = {};
1780
+ for (const [apiName, action] of Object.entries(actions)) {
1781
+ result[apiName] = {
1782
+ scriptActionDefinition: {
1783
+ scriptMethodName: action.methodName,
1784
+ inputProperties: mapProperties(action.input?.jsonSchema),
1785
+ outputProperties: mapProperties(action.output.jsonSchema)
1786
+ }
1787
+ };
1788
+ }
1789
+ return result;
1790
+ }
1791
+ function mapProperties(schema) {
1792
+ const result = {};
1793
+ const requiredSet = new Set(schema?.required ?? []);
1794
+ if (!schema?.properties) return result;
1795
+ const defs = schema.$defs ?? {};
1796
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
1797
+ result[key] = toFieldSchema(resolveRef(propSchema, defs), requiredSet.has(key));
1798
+ }
1799
+ return result;
1800
+ }
1801
+ function resolveRef(schema, defs) {
1802
+ const ref = schema.$ref;
1803
+ if (!ref) return schema;
1804
+ if (!ref.startsWith("#/$defs/")) return schema;
1805
+ const defName = ref.slice("#/$defs/".length);
1806
+ const defSchema = defs[defName];
1807
+ if (!defSchema) return schema;
1808
+ const { $ref: _, ...rest } = schema;
1809
+ return { ...defSchema, ...rest };
1810
+ }
1811
+ function toFieldSchema(schema, required) {
1812
+ const enumValues = extractEnumValues(schema);
1813
+ const refTarget = schema.type === "object" ? getRefTargetType(schema) : null;
1814
+ const dataType = resolveDataType(schema, enumValues, refTarget);
1815
+ const fieldSchema = {
1816
+ type: dataType,
1817
+ values: [],
1818
+ valuesPresence: FieldPresence.NOT_PRESENT
1819
+ };
1820
+ if (schema.title !== void 0) fieldSchema.title = schema.title;
1821
+ if (schema.description !== void 0) fieldSchema.description = schema.description;
1822
+ if (required) fieldSchema.required = true;
1823
+ if (schema.deprecated) fieldSchema.deprecated = true;
1824
+ if (refTarget !== null) fieldSchema.referencedType = refTarget;
1825
+ if (schema.minLength !== void 0) fieldSchema.minLength = schema.minLength;
1826
+ if (schema.maxLength !== void 0) fieldSchema.maxLength = schema.maxLength;
1827
+ if (schema.minimum !== void 0) fieldSchema.minimum = toValueBoundary(schema.minimum);
1828
+ if (schema.exclusiveMinimum !== void 0) {
1829
+ fieldSchema.exclusiveMinimum = toValueBoundary(schema.exclusiveMinimum);
1830
+ }
1831
+ if (schema.maximum !== void 0) fieldSchema.maximum = toValueBoundary(schema.maximum);
1832
+ if (schema.exclusiveMaximum !== void 0) {
1833
+ fieldSchema.exclusiveMaximum = toValueBoundary(schema.exclusiveMaximum);
1834
+ }
1835
+ if (enumValues) {
1836
+ fieldSchema.values = enumValues;
1837
+ fieldSchema.valuesPresence = FieldPresence.PRESENT;
1838
+ }
1839
+ if (schema.default !== void 0) {
1840
+ fieldSchema.default = toDefaultValue(schema.default, fieldSchema.type);
1841
+ }
1842
+ return fieldSchema;
1843
+ }
1844
+ function resolveDataType(schema, enumValues, refTarget) {
1845
+ if (enumValues) return DataType.ENUM_TYPE;
1846
+ if (refTarget !== null) return DataType.OBJECT_REFERENCE_TYPE;
1847
+ switch (schema.type) {
1848
+ case "string":
1849
+ if (schema.format === "date-time") return DataType.DATETIME_TYPE;
1850
+ return DataType.STRING_TYPE;
1851
+ case "integer":
1852
+ case "number":
1853
+ return DataType.INTEGER_TYPE;
1854
+ case "boolean":
1855
+ return DataType.BOOLEAN_TYPE;
1856
+ default:
1857
+ return DataType.UNSPECIFIED;
1858
+ }
1859
+ }
1860
+ function getRefTargetType(schema) {
1861
+ if (!schema.properties) return null;
1862
+ const properties = schema.properties;
1863
+ const realProps = Object.keys(properties).filter((k) => !k.startsWith("__"));
1864
+ const allowedProps = /* @__PURE__ */ new Set(["id", "type", "url"]);
1865
+ if (realProps.length === 0 || realProps.some((k) => !allowedProps.has(k))) return null;
1866
+ const idProp = properties.id;
1867
+ const typeProp = properties.type;
1868
+ if (!idProp || !typeProp) return null;
1869
+ if (idProp.type !== "string") return null;
1870
+ const requiredSet = new Set(schema.required ?? []);
1871
+ if (!requiredSet.has("id") || !requiredSet.has("type")) return null;
1872
+ return extractSingleLiteral(typeProp);
1873
+ }
1874
+ function extractSingleLiteral(schema) {
1875
+ if (Array.isArray(schema.enum) && schema.enum.length === 1 && typeof schema.enum[0] === "string") {
1876
+ return schema.enum[0];
1877
+ }
1878
+ if (Array.isArray(schema.oneOf) && schema.oneOf.length === 1 && schema.oneOf.every(
1879
+ (item) => "const" in item && typeof item.const === "string"
1880
+ )) {
1881
+ const values = schema.oneOf.map(
1882
+ (item) => String(item.const)
1883
+ );
1884
+ return values[0] ?? null;
1885
+ }
1886
+ return null;
1887
+ }
1888
+ function extractEnumValues(schema) {
1889
+ if (schema.enum) {
1890
+ return schema.enum.map(String);
1891
+ }
1892
+ if (Array.isArray(schema.oneOf) && schema.oneOf.length > 0 && schema.oneOf.every((item) => "const" in item)) {
1893
+ return schema.oneOf.map((item) => String(item.const));
1894
+ }
1895
+ return null;
1896
+ }
1897
+ function toValueBoundary(value) {
1898
+ if (!Number.isInteger(value)) {
1899
+ throw new Error(
1900
+ `ValueBoundary only supports integers, got ${String(value)}. Custom object numeric fields are integer-only.`
1901
+ );
1902
+ }
1903
+ return { value: { $case: "integerBoundary", value } };
1904
+ }
1905
+ function toDefaultValue(value, dataType) {
1906
+ if (dataType === DataType.STRING_TYPE && typeof value === "string") {
1907
+ return { value: { $case: "stringDefault", value } };
1908
+ }
1909
+ if (dataType === DataType.INTEGER_TYPE && typeof value === "number") {
1910
+ return { value: { $case: "integerDefault", value } };
1911
+ }
1912
+ if (dataType === DataType.BOOLEAN_TYPE && typeof value === "boolean") {
1913
+ return { value: { $case: "booleanDefault", value } };
1914
+ }
1915
+ return { value: void 0 };
1916
+ }
1917
+
1918
+ // src/custom-objects/build-definitions.ts
1919
+ var import_extensibility_tool_utils7 = require("@stripe/extensibility-tool-utils");
1920
+ async function analyzeAndInjectManifest(options) {
1921
+ const context = options.context ?? (0, import_extensibility_tool_utils7._createCliContext)();
1922
+ const manifestPath = options.manifestPath ?? "stripe-app.yaml";
1923
+ const projectRoot = options.projectRoot ?? path2.dirname(path2.resolve(manifestPath));
1924
+ const resolvedManifestPath = path2.resolve(manifestPath);
1925
+ const manifest = await _ManifestV2.load(resolvedManifestPath);
1926
+ const customObjects = manifest.getCustomObjects();
1927
+ if (customObjects.length === 0) {
1928
+ return null;
1929
+ }
1930
+ const targets = customObjects.map((obj) => ({
1931
+ modulePath: path2.resolve(projectRoot, obj.path),
1932
+ exportName: obj.name
1933
+ }));
1934
+ context.ux.log(
1935
+ `Building ${String(customObjects.length)} custom object definition(s)...`
1936
+ );
1937
+ for (const obj of customObjects) {
1938
+ context.ux.log(` ${obj.path}#${obj.name}`);
1939
+ }
1940
+ const packageBuild = customObjectTools.buildCustomObjectPackage({ targets });
1941
+ const errorDiagnostics = packageBuild.diagnostics.filter(
1942
+ (diagnostic) => diagnostic.severity === "error"
1943
+ );
1944
+ if (errorDiagnostics.length > 0) {
1945
+ const details = errorDiagnostics.map((diagnostic) => diagnostic.message).join("; ");
1946
+ throw new Error(details);
1947
+ }
1948
+ const coPackageJsonPath = path2.join(projectRoot, "custom-objects", "package.json");
1949
+ const coVersion = readPackageVersion(coPackageJsonPath);
1950
+ const coDependencies = readPackageDependencies(coPackageJsonPath);
1951
+ let manifestModified = false;
1952
+ for (const obj of customObjects) {
1953
+ const absoluteModulePath = path2.resolve(projectRoot, obj.path);
1954
+ const objectKey = customObjectTools.toObjectKey({
1955
+ modulePath: absoluteModulePath,
1956
+ exportName: obj.name
1957
+ });
1958
+ const artifact = packageBuild.objects[objectKey];
1959
+ if (!artifact) {
1960
+ throw new Error(
1961
+ `Package build did not return an artifact for "${obj.name}" (${obj.path}).`
1962
+ );
1963
+ }
1964
+ const hasActions = Object.keys(artifact.actions).length > 0;
1965
+ if (!hasActions) {
1966
+ continue;
1967
+ }
1968
+ const transformedCode = packageBuild.transformedModules[absoluteModulePath];
1969
+ if (!transformedCode) {
1970
+ continue;
1971
+ }
1972
+ const extensionId = `methods.${obj.id}`;
1973
+ const scriptContentPath = `extensions/${extensionId}/src/index.ts`;
1974
+ const templateOutput = await _templateManager.generate(
1975
+ "extend.objects.custom_objects",
1976
+ { id: extensionId, name: `${obj.id} methods`, version: coVersion }
1977
+ );
1978
+ manifest.addOrUpdateExtension({
1979
+ id: extensionId,
1980
+ name: `${obj.id} methods`,
1981
+ interface_id: "extend.objects.custom_objects",
1982
+ version: manifest.getRawData().version,
1983
+ script: {
1984
+ type: "typescript",
1985
+ content: scriptContentPath
1986
+ },
1987
+ permissions: [],
1988
+ methods: templateOutput.methods
1989
+ });
1990
+ manifestModified = true;
1991
+ }
1992
+ if (manifestModified) {
1993
+ await manifest.save();
1994
+ context.ux.log(" Updated stripe-app.yaml with methods extension entries");
1995
+ }
1996
+ return {
1997
+ packageBuild,
1998
+ customObjects,
1999
+ manifest,
2000
+ coVersion,
2001
+ coDependencies,
2002
+ projectRoot
2003
+ };
2004
+ }
2005
+ async function writeCustomObjectArtifacts(options, state) {
2006
+ const context = options.context ?? (0, import_extensibility_tool_utils7._createCliContext)();
2007
+ const targetPath = path2.resolve(options.targetPath);
2008
+ const { packageBuild, customObjects, coVersion, coDependencies, projectRoot } = state;
2009
+ for (const obj of customObjects) {
2010
+ const absoluteModulePath = path2.resolve(projectRoot, obj.path);
2011
+ const objectKey = customObjectTools.toObjectKey({
2012
+ modulePath: absoluteModulePath,
2013
+ exportName: obj.name
2014
+ });
2015
+ const artifact = packageBuild.objects[objectKey];
2016
+ if (!artifact) {
2017
+ throw new Error(
2018
+ `Package build did not return an artifact for "${obj.name}" (${obj.path}).`
2019
+ );
2020
+ }
2021
+ const requestJson = toUpsertRequestJson(artifact);
2022
+ const definitionPath = path2.join(targetPath, obj.path.replace(/\.tsx?$/, ".json"));
2023
+ fs2.mkdirSync(path2.dirname(definitionPath), { recursive: true });
2024
+ fs2.writeFileSync(definitionPath, JSON.stringify(requestJson, null, 2) + "\n");
2025
+ context.ux.log(` \u2192 ${path2.relative(targetPath, definitionPath)}`);
2026
+ const hasActions = Object.keys(artifact.actions).length > 0;
2027
+ if (!hasActions) {
2028
+ continue;
2029
+ }
2030
+ const transformedCode = packageBuild.transformedModules[absoluteModulePath];
2031
+ if (!transformedCode) {
2032
+ continue;
2033
+ }
2034
+ const extensionId = `methods.${obj.id}`;
2035
+ const scriptContentPath = `extensions/${extensionId}/src/index.ts`;
2036
+ const templateOutput = await _templateManager.generate(
2037
+ "extend.objects.custom_objects",
2038
+ { id: extensionId, name: `${obj.id} methods`, version: coVersion }
2039
+ );
2040
+ for (const file of templateOutput.files) {
2041
+ const filePath = path2.join(targetPath, file.path);
2042
+ fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
2043
+ if (file.path.endsWith("package.json") && coDependencies) {
2044
+ const pkg = JSON.parse(file.content);
2045
+ pkg.dependencies = coDependencies;
2046
+ fs2.writeFileSync(filePath, JSON.stringify(pkg, null, 2) + "\n");
2047
+ } else {
2048
+ fs2.writeFileSync(filePath, file.content);
2049
+ }
2050
+ context.ux.log(` \u2192 ${path2.relative(targetPath, filePath)}`);
2051
+ }
2052
+ const transformedPath = path2.join(targetPath, scriptContentPath);
2053
+ fs2.mkdirSync(path2.dirname(transformedPath), { recursive: true });
2054
+ fs2.writeFileSync(transformedPath, transformedCode);
2055
+ context.ux.log(` \u2192 ${path2.relative(targetPath, transformedPath)}`);
2056
+ }
2057
+ }
2058
+ function readPackageJson(packageJsonPath) {
2059
+ try {
2060
+ const raw = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
2061
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
2062
+ return raw;
2063
+ }
2064
+ } catch {
2065
+ }
2066
+ return void 0;
2067
+ }
2068
+ function readPackageVersion(packageJsonPath) {
2069
+ const pkg = readPackageJson(packageJsonPath);
2070
+ return typeof pkg?.version === "string" ? pkg.version : "0.0.0";
2071
+ }
2072
+ function readPackageDependencies(packageJsonPath) {
2073
+ const pkg = readPackageJson(packageJsonPath);
2074
+ const deps = pkg?.dependencies;
2075
+ if (typeof deps === "object" && deps !== null && !Array.isArray(deps)) {
2076
+ return deps;
2077
+ }
2078
+ return void 0;
2079
+ }
2080
+
2081
+ // src/bin/create-upload-image.ts
2082
+ async function main() {
2083
+ const targetPath = process.argv[2];
2084
+ if (!targetPath) {
2085
+ console.error("Usage: create-upload-image <target-path>");
2086
+ process.exit(1);
2087
+ }
2088
+ let state = null;
2089
+ const manifestPath = "stripe-app.yaml";
2090
+ if (fs3.existsSync(manifestPath)) {
2091
+ state = await analyzeAndInjectManifest({ manifestPath });
2092
+ }
2093
+ fs3.mkdirSync(targetPath, { recursive: true });
2094
+ const tarball = path3.join(os2.tmpdir(), `upload-image-${String(Date.now())}.tgz`);
2095
+ try {
2096
+ (0, import_node_child_process2.execSync)(`pnpm pack --out ${JSON.stringify(tarball)}`, { stdio: "pipe" });
2097
+ (0, import_node_child_process2.execSync)(
2098
+ `tar -xzf ${JSON.stringify(tarball)} -C ${JSON.stringify(targetPath)} --strip-components=1`,
2099
+ { stdio: "pipe" }
2100
+ );
2101
+ } finally {
2102
+ try {
2103
+ fs3.unlinkSync(tarball);
2104
+ } catch {
2105
+ }
2106
+ }
2107
+ const extensionsDir = "extensions";
2108
+ if (fs3.existsSync(extensionsDir)) {
2109
+ const entries = fs3.readdirSync(extensionsDir, { withFileTypes: true });
2110
+ for (const entry of entries) {
2111
+ if (!entry.isDirectory()) {
2112
+ continue;
2113
+ }
2114
+ const generatedSrc = path3.join(extensionsDir, entry.name, "generated");
2115
+ if (!fs3.existsSync(generatedSrc)) {
2116
+ continue;
2117
+ }
2118
+ const generatedDst = path3.join(targetPath, extensionsDir, entry.name, "generated");
2119
+ fs3.mkdirSync(generatedDst, { recursive: true });
2120
+ fs3.cpSync(generatedSrc, generatedDst, { recursive: true });
2121
+ }
2122
+ }
2123
+ if (state) {
2124
+ await writeCustomObjectArtifacts({ targetPath }, state);
2125
+ }
2126
+ const imageMetadata = JSON.stringify(
2127
+ { image: { version: "1.0", built: (/* @__PURE__ */ new Date()).toISOString() } },
2128
+ null,
2129
+ 2
2130
+ );
2131
+ fs3.writeFileSync(path3.join(targetPath, ".image.json"), imageMetadata + "\n");
2132
+ }
2133
+ main().catch((err) => {
2134
+ console.error(err);
2135
+ process.exit(1);
2136
+ });