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