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