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