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