@payloadcms/plugin-stripe 4.0.0-canary.33 → 4.0.0-canary.34

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 (45) hide show
  1. package/dist/admin.d.ts.map +1 -1
  2. package/dist/admin.js +4 -2
  3. package/dist/admin.js.map +1 -1
  4. package/dist/exports/types.d.ts +1 -1
  5. package/dist/exports/types.d.ts.map +1 -1
  6. package/dist/exports/types.js.map +1 -1
  7. package/dist/fields/getFields.d.ts.map +1 -1
  8. package/dist/fields/getFields.js +8 -0
  9. package/dist/fields/getFields.js.map +1 -1
  10. package/dist/hooks/createNewInStripe.d.ts.map +1 -1
  11. package/dist/hooks/createNewInStripe.js +6 -7
  12. package/dist/hooks/createNewInStripe.js.map +1 -1
  13. package/dist/hooks/syncExistingWithStripe.d.ts.map +1 -1
  14. package/dist/hooks/syncExistingWithStripe.js +6 -7
  15. package/dist/hooks/syncExistingWithStripe.js.map +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +5 -2
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.spec.js +73 -0
  20. package/dist/index.spec.js.map +1 -0
  21. package/dist/routes/rest.d.ts +3 -3
  22. package/dist/routes/rest.d.ts.map +1 -1
  23. package/dist/routes/rest.js +104 -27
  24. package/dist/routes/rest.js.map +1 -1
  25. package/dist/routes/rest.spec.js +275 -0
  26. package/dist/routes/rest.spec.js.map +1 -0
  27. package/dist/types.d.ts +10 -3
  28. package/dist/types.d.ts.map +1 -1
  29. package/dist/types.js.map +1 -1
  30. package/dist/utilities/deepen.d.ts +6 -0
  31. package/dist/utilities/deepen.d.ts.map +1 -1
  32. package/dist/utilities/deepen.js +33 -3
  33. package/dist/utilities/deepen.js.map +1 -1
  34. package/dist/utilities/deepen.spec.js +110 -0
  35. package/dist/utilities/deepen.spec.js.map +1 -0
  36. package/dist/utilities/sanitizeStripeRESTConfig.d.ts +5 -0
  37. package/dist/utilities/sanitizeStripeRESTConfig.d.ts.map +1 -0
  38. package/dist/utilities/sanitizeStripeRESTConfig.js +25 -0
  39. package/dist/utilities/sanitizeStripeRESTConfig.js.map +1 -0
  40. package/dist/utilities/sanitizeStripeRESTConfig.spec.js +116 -0
  41. package/dist/utilities/sanitizeStripeRESTConfig.spec.js.map +1 -0
  42. package/dist/webhooks/handleCreatedOrUpdated.d.ts.map +1 -1
  43. package/dist/webhooks/handleCreatedOrUpdated.js +6 -6
  44. package/dist/webhooks/handleCreatedOrUpdated.js.map +1 -1
  45. package/package.json +6 -6
@@ -0,0 +1,275 @@
1
+ import { UnauthorizedError } from 'payload';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ const mocks = vi.hoisted(()=>({
4
+ addDataAndFileToRequest: vi.fn(),
5
+ canAccessAdmin: vi.fn(),
6
+ stripeProxy: vi.fn()
7
+ }));
8
+ vi.mock('payload', async (importOriginal)=>{
9
+ const payload = await importOriginal();
10
+ return {
11
+ ...payload,
12
+ addDataAndFileToRequest: mocks.addDataAndFileToRequest,
13
+ canAccessAdmin: mocks.canAccessAdmin
14
+ };
15
+ });
16
+ vi.mock('../utilities/stripeProxy.js', ()=>({
17
+ stripeProxy: mocks.stripeProxy
18
+ }));
19
+ import { stripeREST } from './rest.js';
20
+ const loggerError = vi.fn();
21
+ const createPluginConfig = ({ access } = {})=>({
22
+ rest: {
23
+ access,
24
+ allowedMethods: [
25
+ 'subscriptions.list'
26
+ ]
27
+ },
28
+ stripeSecretKey: 'sk_test_example',
29
+ sync: []
30
+ });
31
+ const createRequest = ({ stripeArgs = [], stripeMethod = 'subscriptions.list', user = {
32
+ collection: 'users',
33
+ id: 'user-1'
34
+ } } = {})=>({
35
+ data: {
36
+ stripeArgs,
37
+ stripeMethod
38
+ },
39
+ payload: {
40
+ logger: {
41
+ error: loggerError
42
+ }
43
+ },
44
+ user
45
+ });
46
+ describe('stripeREST', ()=>{
47
+ beforeEach(()=>{
48
+ vi.clearAllMocks();
49
+ mocks.addDataAndFileToRequest.mockResolvedValue(undefined);
50
+ mocks.canAccessAdmin.mockResolvedValue(undefined);
51
+ mocks.stripeProxy.mockResolvedValue({
52
+ data: {
53
+ id: 'sub_123'
54
+ },
55
+ status: 200
56
+ });
57
+ });
58
+ it('should return 401 for an anonymous request without invoking Stripe', async ()=>{
59
+ const req = createRequest({
60
+ user: null
61
+ });
62
+ const response = await stripeREST({
63
+ pluginConfig: createPluginConfig(),
64
+ req
65
+ });
66
+ expect(response.status).toBe(401);
67
+ expect(mocks.addDataAndFileToRequest).toHaveBeenCalledWith(req);
68
+ expect(mocks.canAccessAdmin).not.toHaveBeenCalled();
69
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
70
+ });
71
+ it('should return 403 when default admin access denies the user', async ()=>{
72
+ mocks.canAccessAdmin.mockRejectedValue(new UnauthorizedError());
73
+ const req = createRequest();
74
+ const response = await stripeREST({
75
+ pluginConfig: createPluginConfig(),
76
+ req
77
+ });
78
+ expect(response.status).toBe(403);
79
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
80
+ });
81
+ it('should return a generic 500 when default admin access throws unexpectedly', async ()=>{
82
+ const adminAccessError = new Error('sensitive admin access detail');
83
+ mocks.canAccessAdmin.mockRejectedValue(adminAccessError);
84
+ const req = createRequest();
85
+ const response = await stripeREST({
86
+ pluginConfig: createPluginConfig(),
87
+ req
88
+ });
89
+ const body = await response.json();
90
+ expect(response.status).toBe(500);
91
+ expect(body).toEqual({
92
+ message: 'Internal server error'
93
+ });
94
+ expect(JSON.stringify(body)).not.toContain('sensitive admin access detail');
95
+ expect(loggerError).toHaveBeenCalledWith({
96
+ err: adminAccessError,
97
+ msg: 'An unexpected error occurred in the Stripe plugin REST handler.'
98
+ });
99
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
100
+ });
101
+ it('should invoke an exactly listed method for an admin user', async ()=>{
102
+ const req = createRequest({
103
+ stripeArgs: [
104
+ {
105
+ limit: 10
106
+ }
107
+ ]
108
+ });
109
+ const response = await stripeREST({
110
+ pluginConfig: createPluginConfig(),
111
+ req
112
+ });
113
+ expect(response.status).toBe(200);
114
+ expect(mocks.stripeProxy).toHaveBeenCalledWith({
115
+ stripeArgs: [
116
+ {
117
+ limit: 10
118
+ }
119
+ ],
120
+ stripeMethod: 'subscriptions.list',
121
+ stripeSecretKey: 'sk_test_example'
122
+ });
123
+ });
124
+ it('should allow a non-admin when an asynchronous custom access callback permits it', async ()=>{
125
+ const access = vi.fn(async ()=>true);
126
+ const req = createRequest();
127
+ const response = await stripeREST({
128
+ pluginConfig: createPluginConfig({
129
+ access
130
+ }),
131
+ req
132
+ });
133
+ expect(response.status).toBe(200);
134
+ expect(access).toHaveBeenCalledWith({
135
+ req
136
+ });
137
+ expect(mocks.canAccessAdmin).not.toHaveBeenCalled();
138
+ });
139
+ it('should return 403 when custom access denies the user', async ()=>{
140
+ const access = vi.fn(async ()=>false);
141
+ const req = createRequest();
142
+ const response = await stripeREST({
143
+ pluginConfig: createPluginConfig({
144
+ access
145
+ }),
146
+ req
147
+ });
148
+ expect(response.status).toBe(403);
149
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
150
+ });
151
+ it('should return a generic 500 when custom access throws', async ()=>{
152
+ const access = vi.fn(async ()=>{
153
+ throw new Error('sensitive access detail');
154
+ });
155
+ const req = createRequest();
156
+ const response = await stripeREST({
157
+ pluginConfig: createPluginConfig({
158
+ access
159
+ }),
160
+ req
161
+ });
162
+ const body = await response.text();
163
+ expect(response.status).toBe(500);
164
+ expect(body).not.toContain('sensitive access detail');
165
+ expect(loggerError).toHaveBeenCalledWith({
166
+ err: expect.any(Error),
167
+ msg: 'An unexpected error occurred in the Stripe plugin REST handler.'
168
+ });
169
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
170
+ });
171
+ it.each([
172
+ 'customers.list',
173
+ 'subscriptions.cancel',
174
+ 'subscriptions.*'
175
+ ])('should return 400 for unlisted method %s without invoking Stripe', async (stripeMethod)=>{
176
+ const req = createRequest({
177
+ stripeMethod
178
+ });
179
+ const response = await stripeREST({
180
+ pluginConfig: createPluginConfig(),
181
+ req
182
+ });
183
+ expect(response.status).toBe(400);
184
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
185
+ });
186
+ it.each([
187
+ [
188
+ ''
189
+ ],
190
+ [
191
+ ' '
192
+ ],
193
+ [
194
+ 123
195
+ ],
196
+ [
197
+ null
198
+ ]
199
+ ])('should return 400 for malformed method %s without invoking Stripe', async (stripeMethod)=>{
200
+ const req = createRequest({
201
+ stripeMethod
202
+ });
203
+ const response = await stripeREST({
204
+ pluginConfig: createPluginConfig(),
205
+ req
206
+ });
207
+ expect(response.status).toBe(400);
208
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
209
+ });
210
+ it('should return 400 for a missing method without invoking Stripe', async ()=>{
211
+ const req = createRequest();
212
+ delete req.data?.stripeMethod;
213
+ const response = await stripeREST({
214
+ pluginConfig: createPluginConfig(),
215
+ req
216
+ });
217
+ expect(response.status).toBe(400);
218
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
219
+ });
220
+ it('should return 400 for non-array Stripe arguments without invoking Stripe', async ()=>{
221
+ const req = createRequest({
222
+ stripeArgs: {
223
+ limit: 10
224
+ }
225
+ });
226
+ const response = await stripeREST({
227
+ pluginConfig: createPluginConfig(),
228
+ req
229
+ });
230
+ expect(response.status).toBe(400);
231
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
232
+ });
233
+ it('should evaluate access before revealing whether a method is listed', async ()=>{
234
+ mocks.canAccessAdmin.mockRejectedValue(new UnauthorizedError());
235
+ const req = createRequest({
236
+ stripeMethod: 'customers.delete'
237
+ });
238
+ const response = await stripeREST({
239
+ pluginConfig: createPluginConfig(),
240
+ req
241
+ });
242
+ expect(response.status).toBe(403);
243
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
244
+ });
245
+ it('should preserve the Stripe proxy response status and body after all guards pass', async ()=>{
246
+ mocks.stripeProxy.mockResolvedValue({
247
+ message: 'Stripe request failed',
248
+ status: 404
249
+ });
250
+ const req = createRequest();
251
+ const response = await stripeREST({
252
+ pluginConfig: createPluginConfig(),
253
+ req
254
+ });
255
+ expect(response.status).toBe(404);
256
+ expect(await response.json()).toEqual({
257
+ message: 'Stripe request failed',
258
+ status: 404
259
+ });
260
+ });
261
+ it('should return a generic 500 when request parsing fails', async ()=>{
262
+ mocks.addDataAndFileToRequest.mockRejectedValue(new Error('sensitive parser detail'));
263
+ const req = createRequest();
264
+ const response = await stripeREST({
265
+ pluginConfig: createPluginConfig(),
266
+ req
267
+ });
268
+ const body = await response.text();
269
+ expect(response.status).toBe(500);
270
+ expect(body).not.toContain('sensitive parser detail');
271
+ expect(mocks.stripeProxy).not.toHaveBeenCalled();
272
+ });
273
+ });
274
+
275
+ //# sourceMappingURL=rest.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/routes/rest.spec.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { UnauthorizedError } from 'payload'\nimport { beforeEach, describe, expect, it, vi } from 'vitest'\n\nimport type { SanitizedStripePluginConfig, StripeRESTAccess } from '../types.js'\n\nconst mocks = vi.hoisted(() => ({\n addDataAndFileToRequest: vi.fn(),\n canAccessAdmin: vi.fn(),\n stripeProxy: vi.fn(),\n}))\n\nvi.mock('payload', async (importOriginal) => {\n const payload = await importOriginal<typeof import('payload')>()\n\n return {\n ...payload,\n addDataAndFileToRequest: mocks.addDataAndFileToRequest,\n canAccessAdmin: mocks.canAccessAdmin,\n }\n})\n\nvi.mock('../utilities/stripeProxy.js', () => ({\n stripeProxy: mocks.stripeProxy,\n}))\n\nimport { stripeREST } from './rest.js'\n\nconst loggerError = vi.fn()\n\nconst createPluginConfig = ({\n access,\n}: {\n access?: StripeRESTAccess\n} = {}): SanitizedStripePluginConfig => ({\n rest: {\n access,\n allowedMethods: ['subscriptions.list'],\n },\n stripeSecretKey: 'sk_test_example',\n sync: [],\n})\n\nconst createRequest = ({\n stripeArgs = [],\n stripeMethod = 'subscriptions.list',\n user = { collection: 'users', id: 'user-1' },\n}: {\n stripeArgs?: unknown\n stripeMethod?: unknown\n user?: null | { collection: string; id: string }\n} = {}): PayloadRequest =>\n ({\n data: { stripeArgs, stripeMethod },\n payload: {\n logger: { error: loggerError },\n },\n user,\n }) as unknown as PayloadRequest\n\ndescribe('stripeREST', () => {\n beforeEach(() => {\n vi.clearAllMocks()\n mocks.addDataAndFileToRequest.mockResolvedValue(undefined)\n mocks.canAccessAdmin.mockResolvedValue(undefined)\n mocks.stripeProxy.mockResolvedValue({ data: { id: 'sub_123' }, status: 200 })\n })\n\n it('should return 401 for an anonymous request without invoking Stripe', async () => {\n const req = createRequest({ user: null })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(401)\n expect(mocks.addDataAndFileToRequest).toHaveBeenCalledWith(req)\n expect(mocks.canAccessAdmin).not.toHaveBeenCalled()\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should return 403 when default admin access denies the user', async () => {\n mocks.canAccessAdmin.mockRejectedValue(new UnauthorizedError())\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(403)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should return a generic 500 when default admin access throws unexpectedly', async () => {\n const adminAccessError = new Error('sensitive admin access detail')\n\n mocks.canAccessAdmin.mockRejectedValue(adminAccessError)\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n const body = await response.json()\n\n expect(response.status).toBe(500)\n expect(body).toEqual({ message: 'Internal server error' })\n expect(JSON.stringify(body)).not.toContain('sensitive admin access detail')\n expect(loggerError).toHaveBeenCalledWith({\n err: adminAccessError,\n msg: 'An unexpected error occurred in the Stripe plugin REST handler.',\n })\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should invoke an exactly listed method for an admin user', async () => {\n const req = createRequest({ stripeArgs: [{ limit: 10 }] })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(200)\n expect(mocks.stripeProxy).toHaveBeenCalledWith({\n stripeArgs: [{ limit: 10 }],\n stripeMethod: 'subscriptions.list',\n stripeSecretKey: 'sk_test_example',\n })\n })\n\n it('should allow a non-admin when an asynchronous custom access callback permits it', async () => {\n const access = vi.fn(async () => true)\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig({ access }), req })\n\n expect(response.status).toBe(200)\n expect(access).toHaveBeenCalledWith({ req })\n expect(mocks.canAccessAdmin).not.toHaveBeenCalled()\n })\n\n it('should return 403 when custom access denies the user', async () => {\n const access = vi.fn(async () => false)\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig({ access }), req })\n\n expect(response.status).toBe(403)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should return a generic 500 when custom access throws', async () => {\n const access = vi.fn(async () => {\n throw new Error('sensitive access detail')\n })\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig({ access }), req })\n const body = await response.text()\n\n expect(response.status).toBe(500)\n expect(body).not.toContain('sensitive access detail')\n expect(loggerError).toHaveBeenCalledWith({\n err: expect.any(Error),\n msg: 'An unexpected error occurred in the Stripe plugin REST handler.',\n })\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it.each(['customers.list', 'subscriptions.cancel', 'subscriptions.*'])(\n 'should return 400 for unlisted method %s without invoking Stripe',\n async (stripeMethod) => {\n const req = createRequest({ stripeMethod })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(400)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n },\n )\n\n it.each([[''], [' '], [123], [null]])(\n 'should return 400 for malformed method %s without invoking Stripe',\n async (stripeMethod) => {\n const req = createRequest({ stripeMethod })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(400)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n },\n )\n\n it('should return 400 for a missing method without invoking Stripe', async () => {\n const req = createRequest()\n\n delete req.data?.stripeMethod\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(400)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should return 400 for non-array Stripe arguments without invoking Stripe', async () => {\n const req = createRequest({ stripeArgs: { limit: 10 } })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(400)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should evaluate access before revealing whether a method is listed', async () => {\n mocks.canAccessAdmin.mockRejectedValue(new UnauthorizedError())\n const req = createRequest({ stripeMethod: 'customers.delete' })\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(403)\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n\n it('should preserve the Stripe proxy response status and body after all guards pass', async () => {\n mocks.stripeProxy.mockResolvedValue({ message: 'Stripe request failed', status: 404 })\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n\n expect(response.status).toBe(404)\n expect(await response.json()).toEqual({ message: 'Stripe request failed', status: 404 })\n })\n\n it('should return a generic 500 when request parsing fails', async () => {\n mocks.addDataAndFileToRequest.mockRejectedValue(new Error('sensitive parser detail'))\n const req = createRequest()\n\n const response = await stripeREST({ pluginConfig: createPluginConfig(), req })\n const body = await response.text()\n\n expect(response.status).toBe(500)\n expect(body).not.toContain('sensitive parser detail')\n expect(mocks.stripeProxy).not.toHaveBeenCalled()\n })\n})\n"],"names":["UnauthorizedError","beforeEach","describe","expect","it","vi","mocks","hoisted","addDataAndFileToRequest","fn","canAccessAdmin","stripeProxy","mock","importOriginal","payload","stripeREST","loggerError","createPluginConfig","access","rest","allowedMethods","stripeSecretKey","sync","createRequest","stripeArgs","stripeMethod","user","collection","id","data","logger","error","clearAllMocks","mockResolvedValue","undefined","status","req","response","pluginConfig","toBe","toHaveBeenCalledWith","not","toHaveBeenCalled","mockRejectedValue","adminAccessError","Error","body","json","toEqual","message","JSON","stringify","toContain","err","msg","limit","text","any","each"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -21,19 +21,26 @@ export type SyncConfig = {
21
21
  stripeResourceType: 'customers' | 'products';
22
22
  stripeResourceTypeSingular: 'customer' | 'product';
23
23
  };
24
+ export type StripeRESTAccess = (args: {
25
+ req: PayloadRequest;
26
+ }) => boolean | Promise<boolean>;
27
+ export type StripeRESTConfig = {
28
+ access?: StripeRESTAccess;
29
+ allowedMethods: readonly string[];
30
+ };
24
31
  export type StripePluginConfig = {
25
32
  isTestKey?: boolean;
26
33
  logs?: boolean;
27
- /** @default false */
28
- rest?: boolean;
34
+ rest?: StripeRESTConfig;
29
35
  stripeSecretKey: string;
30
36
  stripeWebhooksEndpointSecret?: string;
31
37
  sync?: SyncConfig[];
32
38
  webhooks?: StripeWebhookHandler | StripeWebhookHandlers;
33
39
  };
34
40
  export type SanitizedStripePluginConfig = {
41
+ rest?: StripeRESTConfig;
35
42
  sync: SyncConfig[];
36
- } & StripePluginConfig;
43
+ } & Omit<StripePluginConfig, 'rest' | 'sync'>;
37
44
  export type StripeProxy = (args: {
38
45
  stripeArgs: any[];
39
46
  stripeMethod: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC/F,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAEhC,MAAM,MAAM,oBAAoB,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE;IACjD,MAAM,EAAE,aAAa,CAAA;IACrB,KAAK,EAAE,CAAC,CAAA;IACR,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAE1B,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,WAAW,EAAE,MAAM,GAAG,oBAAoB,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,cAAc,CAAA;IAC1B,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,kBAAkB,EAAE,WAAW,GAAG,UAAU,CAAA;IAC5C,0BAA0B,EAAE,UAAU,GAAG,SAAS,CAAA;CACnD,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,qBAAqB;IACrB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,eAAe,EAAE,MAAM,CAAA;IACvB,4BAA4B,CAAC,EAAE,MAAM,CAAA;IACrC,IAAI,CAAC,EAAE,UAAU,EAAE,CAAA;IACnB,QAAQ,CAAC,EAAE,oBAAoB,GAAG,qBAAqB,CAAA;CACxD,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,UAAU,EAAE,CAAA;CACnB,GAAG,kBAAkB,CAAA;AAEtB,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE;IAC/B,UAAU,EAAE,GAAG,EAAE,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;CACxB,KAAK,OAAO,CAAC;IACZ,IAAI,CAAC,EAAE,GAAG,CAAA;IACV,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf,CAAC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC/F,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAEhC,MAAM,MAAM,oBAAoB,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE;IACjD,MAAM,EAAE,aAAa,CAAA;IACrB,KAAK,EAAE,CAAC,CAAA;IACR,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,GAAG,EAAE,cAAc,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAE1B,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,WAAW,EAAE,MAAM,GAAG,oBAAoB,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,cAAc,CAAA;IAC1B,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,kBAAkB,EAAE,WAAW,GAAG,UAAU,CAAA;IAC5C,0BAA0B,EAAE,UAAU,GAAG,SAAS,CAAA;CACnD,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,cAAc,CAAA;CAAE,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;AAE5F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,IAAI,CAAC,EAAE,gBAAgB,CAAA;IACvB,eAAe,EAAE,MAAM,CAAA;IACvB,4BAA4B,CAAC,EAAE,MAAM,CAAA;IACrC,IAAI,CAAC,EAAE,UAAU,EAAE,CAAA;IACnB,QAAQ,CAAC,EAAE,oBAAoB,GAAG,qBAAqB,CAAA;CACxD,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,CAAC,EAAE,gBAAgB,CAAA;IACvB,IAAI,EAAE,UAAU,EAAE,CAAA;CACnB,GAAG,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAE7C,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE;IAC/B,UAAU,EAAE,GAAG,EAAE,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;CACxB,KAAK,OAAO,CAAC;IACZ,IAAI,CAAC,EAAE,GAAG,CAAA;IACV,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf,CAAC,CAAA"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug, Payload, Config as PayloadConfig, PayloadRequest } from 'payload'\nimport type Stripe from 'stripe'\n\nexport type StripeWebhookHandler<T = any> = (args: {\n config: PayloadConfig\n event: T\n payload: Payload\n pluginConfig?: StripePluginConfig\n req: PayloadRequest\n stripe: Stripe\n}) => Promise<void> | void\n\nexport type StripeWebhookHandlers = {\n [webhookName: string]: StripeWebhookHandler\n}\n\nexport type FieldSyncConfig = {\n fieldPath: string\n stripeProperty: string\n}\n\nexport type SyncConfig = {\n collection: CollectionSlug\n fields: FieldSyncConfig[]\n stripeResourceType: 'customers' | 'products' // TODO: get this from Stripe types\n stripeResourceTypeSingular: 'customer' | 'product' // TODO: there must be a better way to do this\n}\n\nexport type StripePluginConfig = {\n isTestKey?: boolean\n logs?: boolean\n /** @default false */\n rest?: boolean\n stripeSecretKey: string\n stripeWebhooksEndpointSecret?: string\n sync?: SyncConfig[]\n webhooks?: StripeWebhookHandler | StripeWebhookHandlers\n}\n\nexport type SanitizedStripePluginConfig = {\n sync: SyncConfig[] // convert to required\n} & StripePluginConfig\n\nexport type StripeProxy = (args: {\n stripeArgs: any[]\n stripeMethod: string\n stripeSecretKey: string\n}) => Promise<{\n data?: any\n message?: string\n status: number\n}>\n"],"names":[],"mappings":"AA2CA,WAQE"}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug, Payload, Config as PayloadConfig, PayloadRequest } from 'payload'\nimport type Stripe from 'stripe'\n\nexport type StripeWebhookHandler<T = any> = (args: {\n config: PayloadConfig\n event: T\n payload: Payload\n pluginConfig?: StripePluginConfig\n req: PayloadRequest\n stripe: Stripe\n}) => Promise<void> | void\n\nexport type StripeWebhookHandlers = {\n [webhookName: string]: StripeWebhookHandler\n}\n\nexport type FieldSyncConfig = {\n fieldPath: string\n stripeProperty: string\n}\n\nexport type SyncConfig = {\n collection: CollectionSlug\n fields: FieldSyncConfig[]\n stripeResourceType: 'customers' | 'products' // TODO: get this from Stripe types\n stripeResourceTypeSingular: 'customer' | 'product' // TODO: there must be a better way to do this\n}\n\nexport type StripeRESTAccess = (args: { req: PayloadRequest }) => boolean | Promise<boolean>\n\nexport type StripeRESTConfig = {\n access?: StripeRESTAccess\n allowedMethods: readonly string[]\n}\n\nexport type StripePluginConfig = {\n isTestKey?: boolean\n logs?: boolean\n rest?: StripeRESTConfig\n stripeSecretKey: string\n stripeWebhooksEndpointSecret?: string\n sync?: SyncConfig[]\n webhooks?: StripeWebhookHandler | StripeWebhookHandlers\n}\n\nexport type SanitizedStripePluginConfig = {\n rest?: StripeRESTConfig\n sync: SyncConfig[]\n} & Omit<StripePluginConfig, 'rest' | 'sync'>\n\nexport type StripeProxy = (args: {\n stripeArgs: any[]\n stripeMethod: string\n stripeSecretKey: string\n}) => Promise<{\n data?: any\n message?: string\n status: number\n}>\n"],"names":[],"mappings":"AAkDA,WAQE"}
@@ -1,2 +1,8 @@
1
+ import type { FieldSyncConfig } from '../types.js';
1
2
  export declare const deepen: (obj: Record<string, any>) => Record<string, any>;
3
+ export declare const getSyncedFields: ({ data, fields, source, }: {
4
+ data: object;
5
+ fields: FieldSyncConfig[];
6
+ source: 'payload' | 'stripe';
7
+ }) => Record<string, unknown>;
2
8
  //# sourceMappingURL=deepen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deepen.d.ts","sourceRoot":"","sources":["../../src/utilities/deepen.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,MAAM,QAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAiBnE,CAAA"}
1
+ {"version":3,"file":"deepen.d.ts","sourceRoot":"","sources":["../../src/utilities/deepen.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,eAAO,MAAM,MAAM,QAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAwBnE,CAAA;AAED,eAAO,MAAM,eAAe,8BAIzB;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,eAAe,EAAE,CAAA;IACzB,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAA;CAC7B,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAczB,CAAA"}
@@ -1,21 +1,51 @@
1
1
  // converts an object of dot notation keys to a nested object
2
2
  // i.e. { 'price.stripePriceID': '123' } to { price: { stripePriceID: '123' } }
3
+ import { hasUnsupportedFieldPathSegment, setOwnProperty } from 'payload/shared';
3
4
  export const deepen = (obj)=>{
4
5
  const result = {};
5
- for(const key in obj){
6
+ for (const key of Object.keys(obj)){
6
7
  const value = obj[key];
7
8
  const keys = key.split('.');
9
+ if (hasUnsupportedFieldPathSegment({
10
+ segments: keys
11
+ })) {
12
+ throw new Error('Invalid field path.');
13
+ }
8
14
  let current = result;
9
15
  keys.forEach((k, index)=>{
10
16
  if (index === keys.length - 1) {
11
- current[k] = value;
17
+ setOwnProperty({
18
+ key: k,
19
+ target: current,
20
+ value
21
+ });
12
22
  } else {
13
- current[k] = current[k] || {};
23
+ if (!Object.hasOwn(current, k) || current[k] === null || typeof current[k] !== 'object') {
24
+ setOwnProperty({
25
+ key: k,
26
+ target: current,
27
+ value: {}
28
+ });
29
+ }
14
30
  current = current[k];
15
31
  }
16
32
  });
17
33
  }
18
34
  return result;
19
35
  };
36
+ export const getSyncedFields = ({ data, fields, source })=>{
37
+ const flattened = Object.create(null);
38
+ for (const { fieldPath, stripeProperty } of fields){
39
+ const sourceProperty = source === 'payload' ? fieldPath : stripeProperty;
40
+ const targetProperty = source === 'payload' ? stripeProperty : fieldPath;
41
+ const value = Object.hasOwn(data, sourceProperty) ? data[sourceProperty] : undefined;
42
+ setOwnProperty({
43
+ key: targetProperty,
44
+ target: flattened,
45
+ value
46
+ });
47
+ }
48
+ return deepen(flattened);
49
+ };
20
50
 
21
51
  //# sourceMappingURL=deepen.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/deepen.ts"],"sourcesContent":["// converts an object of dot notation keys to a nested object\n// i.e. { 'price.stripePriceID': '123' } to { price: { stripePriceID: '123' } }\n\nexport const deepen = (obj: Record<string, any>): Record<string, any> => {\n const result: Record<string, any> = {}\n\n for (const key in obj) {\n const value = obj[key]\n const keys = key.split('.')\n let current = result\n keys.forEach((k, index) => {\n if (index === keys.length - 1) {\n current[k] = value\n } else {\n current[k] = current[k] || {}\n current = current[k]\n }\n })\n }\n return result\n}\n"],"names":["deepen","obj","result","key","value","keys","split","current","forEach","k","index","length"],"mappings":"AAAA,6DAA6D;AAC7D,+EAA+E;AAE/E,OAAO,MAAMA,SAAS,CAACC;IACrB,MAAMC,SAA8B,CAAC;IAErC,IAAK,MAAMC,OAAOF,IAAK;QACrB,MAAMG,QAAQH,GAAG,CAACE,IAAI;QACtB,MAAME,OAAOF,IAAIG,KAAK,CAAC;QACvB,IAAIC,UAAUL;QACdG,KAAKG,OAAO,CAAC,CAACC,GAAGC;YACf,IAAIA,UAAUL,KAAKM,MAAM,GAAG,GAAG;gBAC7BJ,OAAO,CAACE,EAAE,GAAGL;YACf,OAAO;gBACLG,OAAO,CAACE,EAAE,GAAGF,OAAO,CAACE,EAAE,IAAI,CAAC;gBAC5BF,UAAUA,OAAO,CAACE,EAAE;YACtB;QACF;IACF;IACA,OAAOP;AACT,EAAC"}
1
+ {"version":3,"sources":["../../src/utilities/deepen.ts"],"sourcesContent":["// converts an object of dot notation keys to a nested object\n// i.e. { 'price.stripePriceID': '123' } to { price: { stripePriceID: '123' } }\n\nimport { hasUnsupportedFieldPathSegment, setOwnProperty } from 'payload/shared'\n\nimport type { FieldSyncConfig } from '../types.js'\n\nexport const deepen = (obj: Record<string, any>): Record<string, any> => {\n const result: Record<string, any> = {}\n\n for (const key of Object.keys(obj)) {\n const value = obj[key]\n const keys = key.split('.')\n\n if (hasUnsupportedFieldPathSegment({ segments: keys })) {\n throw new Error('Invalid field path.')\n }\n\n let current = result\n keys.forEach((k, index) => {\n if (index === keys.length - 1) {\n setOwnProperty({ key: k, target: current, value })\n } else {\n if (!Object.hasOwn(current, k) || current[k] === null || typeof current[k] !== 'object') {\n setOwnProperty({ key: k, target: current, value: {} })\n }\n current = current[k]\n }\n })\n }\n return result\n}\n\nexport const getSyncedFields = ({\n data,\n fields,\n source,\n}: {\n data: object\n fields: FieldSyncConfig[]\n source: 'payload' | 'stripe'\n}): Record<string, unknown> => {\n const flattened = Object.create(null) as Record<string, unknown>\n\n for (const { fieldPath, stripeProperty } of fields) {\n const sourceProperty = source === 'payload' ? fieldPath : stripeProperty\n const targetProperty = source === 'payload' ? stripeProperty : fieldPath\n const value = Object.hasOwn(data, sourceProperty)\n ? (data as Record<string, unknown>)[sourceProperty]\n : undefined\n\n setOwnProperty({ key: targetProperty, target: flattened, value })\n }\n\n return deepen(flattened)\n}\n"],"names":["hasUnsupportedFieldPathSegment","setOwnProperty","deepen","obj","result","key","Object","keys","value","split","segments","Error","current","forEach","k","index","length","target","hasOwn","getSyncedFields","data","fields","source","flattened","create","fieldPath","stripeProperty","sourceProperty","targetProperty","undefined"],"mappings":"AAAA,6DAA6D;AAC7D,+EAA+E;AAE/E,SAASA,8BAA8B,EAAEC,cAAc,QAAQ,iBAAgB;AAI/E,OAAO,MAAMC,SAAS,CAACC;IACrB,MAAMC,SAA8B,CAAC;IAErC,KAAK,MAAMC,OAAOC,OAAOC,IAAI,CAACJ,KAAM;QAClC,MAAMK,QAAQL,GAAG,CAACE,IAAI;QACtB,MAAME,OAAOF,IAAII,KAAK,CAAC;QAEvB,IAAIT,+BAA+B;YAAEU,UAAUH;QAAK,IAAI;YACtD,MAAM,IAAII,MAAM;QAClB;QAEA,IAAIC,UAAUR;QACdG,KAAKM,OAAO,CAAC,CAACC,GAAGC;YACf,IAAIA,UAAUR,KAAKS,MAAM,GAAG,GAAG;gBAC7Bf,eAAe;oBAAEI,KAAKS;oBAAGG,QAAQL;oBAASJ;gBAAM;YAClD,OAAO;gBACL,IAAI,CAACF,OAAOY,MAAM,CAACN,SAASE,MAAMF,OAAO,CAACE,EAAE,KAAK,QAAQ,OAAOF,OAAO,CAACE,EAAE,KAAK,UAAU;oBACvFb,eAAe;wBAAEI,KAAKS;wBAAGG,QAAQL;wBAASJ,OAAO,CAAC;oBAAE;gBACtD;gBACAI,UAAUA,OAAO,CAACE,EAAE;YACtB;QACF;IACF;IACA,OAAOV;AACT,EAAC;AAED,OAAO,MAAMe,kBAAkB,CAAC,EAC9BC,IAAI,EACJC,MAAM,EACNC,MAAM,EAKP;IACC,MAAMC,YAAYjB,OAAOkB,MAAM,CAAC;IAEhC,KAAK,MAAM,EAAEC,SAAS,EAAEC,cAAc,EAAE,IAAIL,OAAQ;QAClD,MAAMM,iBAAiBL,WAAW,YAAYG,YAAYC;QAC1D,MAAME,iBAAiBN,WAAW,YAAYI,iBAAiBD;QAC/D,MAAMjB,QAAQF,OAAOY,MAAM,CAACE,MAAMO,kBAC9B,AAACP,IAAgC,CAACO,eAAe,GACjDE;QAEJ5B,eAAe;YAAEI,KAAKuB;YAAgBX,QAAQM;YAAWf;QAAM;IACjE;IAEA,OAAON,OAAOqB;AAChB,EAAC"}
@@ -0,0 +1,110 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { deepen, getSyncedFields } from './deepen.js';
3
+ describe('Stripe field mappings', ()=>{
4
+ it('should build constrained mappings from owned values', ()=>{
5
+ const marker = 'constrainedStripeMapping';
6
+ const inheritedSource = 'constrainedStripeSource';
7
+ const originalDescriptor = Object.getOwnPropertyDescriptor(Object.prototype, marker);
8
+ let inheritedGetterCalls = 0;
9
+ let processStateChanged = false;
10
+ let nestedMapping;
11
+ let syncedMapping;
12
+ let webhookMapping;
13
+ try {
14
+ expect(()=>deepen({
15
+ [`__proto__.${marker}`]: 'value'
16
+ })).toThrow('Invalid field path.');
17
+ nestedMapping = deepen(Object.assign(Object.create({
18
+ 'ignored.value': 'inherited'
19
+ }), {
20
+ [`constructor.prototype.${marker}`]: 'local',
21
+ 'profile.displayName': 'Ada'
22
+ }));
23
+ const data = Object.create({
24
+ get [inheritedSource] () {
25
+ inheritedGetterCalls += 1;
26
+ return 'inherited';
27
+ }
28
+ });
29
+ data.displayName = 'Ada';
30
+ syncedMapping = getSyncedFields({
31
+ data,
32
+ fields: [
33
+ {
34
+ fieldPath: inheritedSource,
35
+ stripeProperty: 'metadata.inherited'
36
+ },
37
+ {
38
+ fieldPath: 'constructor',
39
+ stripeProperty: 'constructor'
40
+ },
41
+ {
42
+ fieldPath: 'displayName',
43
+ stripeProperty: `constructor.prototype.${marker}`
44
+ }
45
+ ],
46
+ source: 'payload'
47
+ });
48
+ const stripeData = Object.create({
49
+ get [inheritedSource] () {
50
+ inheritedGetterCalls += 1;
51
+ return 'inherited';
52
+ }
53
+ });
54
+ stripeData.name = 'Ada';
55
+ webhookMapping = getSyncedFields({
56
+ data: stripeData,
57
+ fields: [
58
+ {
59
+ fieldPath: 'profile.inherited',
60
+ stripeProperty: inheritedSource
61
+ },
62
+ {
63
+ fieldPath: 'profile.displayName',
64
+ stripeProperty: 'name'
65
+ }
66
+ ],
67
+ source: 'stripe'
68
+ });
69
+ processStateChanged = Object.hasOwn(Object.prototype, marker);
70
+ } finally{
71
+ if (originalDescriptor) {
72
+ Object.defineProperty(Object.prototype, marker, originalDescriptor);
73
+ } else {
74
+ delete Object.prototype[marker];
75
+ }
76
+ }
77
+ expect(processStateChanged).toBe(false);
78
+ expect(inheritedGetterCalls).toBe(0);
79
+ expect(Object.getOwnPropertyDescriptor(Object.prototype, marker)).toEqual(originalDescriptor);
80
+ expect(nestedMapping).toEqual({
81
+ constructor: {
82
+ prototype: {
83
+ [marker]: 'local'
84
+ }
85
+ },
86
+ profile: {
87
+ displayName: 'Ada'
88
+ }
89
+ });
90
+ expect(Object.hasOwn(nestedMapping, 'ignored')).toBe(false);
91
+ expect(syncedMapping).toEqual({
92
+ constructor: {
93
+ prototype: {
94
+ [marker]: 'Ada'
95
+ }
96
+ },
97
+ metadata: {
98
+ inherited: undefined
99
+ }
100
+ });
101
+ expect(webhookMapping).toEqual({
102
+ profile: {
103
+ displayName: 'Ada',
104
+ inherited: undefined
105
+ }
106
+ });
107
+ });
108
+ });
109
+
110
+ //# sourceMappingURL=deepen.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/deepen.spec.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest'\n\nimport { deepen, getSyncedFields } from './deepen.js'\n\ndescribe('Stripe field mappings', () => {\n it('should build constrained mappings from owned values', () => {\n const marker = 'constrainedStripeMapping'\n const inheritedSource = 'constrainedStripeSource'\n const originalDescriptor = Object.getOwnPropertyDescriptor(Object.prototype, marker)\n let inheritedGetterCalls = 0\n let processStateChanged = false\n let nestedMapping: Record<string, unknown> | undefined\n let syncedMapping: Record<string, unknown> | undefined\n let webhookMapping: Record<string, unknown> | undefined\n\n try {\n expect(() => deepen({ [`__proto__.${marker}`]: 'value' })).toThrow('Invalid field path.')\n\n nestedMapping = deepen(\n Object.assign(Object.create({ 'ignored.value': 'inherited' }), {\n [`constructor.prototype.${marker}`]: 'local',\n 'profile.displayName': 'Ada',\n }),\n )\n\n const data = Object.create({\n get [inheritedSource]() {\n inheritedGetterCalls += 1\n return 'inherited'\n },\n }) as Record<string, unknown>\n data.displayName = 'Ada'\n\n syncedMapping = getSyncedFields({\n data,\n fields: [\n { fieldPath: inheritedSource, stripeProperty: 'metadata.inherited' },\n { fieldPath: 'constructor', stripeProperty: 'constructor' },\n {\n fieldPath: 'displayName',\n stripeProperty: `constructor.prototype.${marker}`,\n },\n ],\n source: 'payload',\n })\n\n const stripeData = Object.create({\n get [inheritedSource]() {\n inheritedGetterCalls += 1\n return 'inherited'\n },\n }) as Record<string, unknown>\n stripeData.name = 'Ada'\n\n webhookMapping = getSyncedFields({\n data: stripeData,\n fields: [\n { fieldPath: 'profile.inherited', stripeProperty: inheritedSource },\n { fieldPath: 'profile.displayName', stripeProperty: 'name' },\n ],\n source: 'stripe',\n })\n processStateChanged = Object.hasOwn(Object.prototype, marker)\n } finally {\n if (originalDescriptor) {\n Object.defineProperty(Object.prototype, marker, originalDescriptor)\n } else {\n delete (Object.prototype as Record<string, unknown>)[marker]\n }\n }\n\n expect(processStateChanged).toBe(false)\n expect(inheritedGetterCalls).toBe(0)\n expect(Object.getOwnPropertyDescriptor(Object.prototype, marker)).toEqual(originalDescriptor)\n expect(nestedMapping).toEqual({\n constructor: {\n prototype: {\n [marker]: 'local',\n },\n },\n profile: {\n displayName: 'Ada',\n },\n })\n expect(Object.hasOwn(nestedMapping as object, 'ignored')).toBe(false)\n expect(syncedMapping).toEqual({\n constructor: {\n prototype: {\n [marker]: 'Ada',\n },\n },\n metadata: {\n inherited: undefined,\n },\n })\n expect(webhookMapping).toEqual({\n profile: {\n displayName: 'Ada',\n inherited: undefined,\n },\n })\n })\n})\n"],"names":["describe","expect","it","deepen","getSyncedFields","marker","inheritedSource","originalDescriptor","Object","getOwnPropertyDescriptor","prototype","inheritedGetterCalls","processStateChanged","nestedMapping","syncedMapping","webhookMapping","toThrow","assign","create","data","displayName","fields","fieldPath","stripeProperty","source","stripeData","name","hasOwn","defineProperty","toBe","toEqual","profile","metadata","inherited","undefined"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SAASC,MAAM,EAAEC,eAAe,QAAQ,cAAa;AAErDJ,SAAS,yBAAyB;IAChCE,GAAG,uDAAuD;QACxD,MAAMG,SAAS;QACf,MAAMC,kBAAkB;QACxB,MAAMC,qBAAqBC,OAAOC,wBAAwB,CAACD,OAAOE,SAAS,EAAEL;QAC7E,IAAIM,uBAAuB;QAC3B,IAAIC,sBAAsB;QAC1B,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QAEJ,IAAI;YACFd,OAAO,IAAME,OAAO;oBAAE,CAAC,CAAC,UAAU,EAAEE,QAAQ,CAAC,EAAE;gBAAQ,IAAIW,OAAO,CAAC;YAEnEH,gBAAgBV,OACdK,OAAOS,MAAM,CAACT,OAAOU,MAAM,CAAC;gBAAE,iBAAiB;YAAY,IAAI;gBAC7D,CAAC,CAAC,sBAAsB,EAAEb,QAAQ,CAAC,EAAE;gBACrC,uBAAuB;YACzB;YAGF,MAAMc,OAAOX,OAAOU,MAAM,CAAC;gBACzB,IAAI,CAACZ,gBAAgB,IAAG;oBACtBK,wBAAwB;oBACxB,OAAO;gBACT;YACF;YACAQ,KAAKC,WAAW,GAAG;YAEnBN,gBAAgBV,gBAAgB;gBAC9Be;gBACAE,QAAQ;oBACN;wBAAEC,WAAWhB;wBAAiBiB,gBAAgB;oBAAqB;oBACnE;wBAAED,WAAW;wBAAeC,gBAAgB;oBAAc;oBAC1D;wBACED,WAAW;wBACXC,gBAAgB,CAAC,sBAAsB,EAAElB,QAAQ;oBACnD;iBACD;gBACDmB,QAAQ;YACV;YAEA,MAAMC,aAAajB,OAAOU,MAAM,CAAC;gBAC/B,IAAI,CAACZ,gBAAgB,IAAG;oBACtBK,wBAAwB;oBACxB,OAAO;gBACT;YACF;YACAc,WAAWC,IAAI,GAAG;YAElBX,iBAAiBX,gBAAgB;gBAC/Be,MAAMM;gBACNJ,QAAQ;oBACN;wBAAEC,WAAW;wBAAqBC,gBAAgBjB;oBAAgB;oBAClE;wBAAEgB,WAAW;wBAAuBC,gBAAgB;oBAAO;iBAC5D;gBACDC,QAAQ;YACV;YACAZ,sBAAsBJ,OAAOmB,MAAM,CAACnB,OAAOE,SAAS,EAAEL;QACxD,SAAU;YACR,IAAIE,oBAAoB;gBACtBC,OAAOoB,cAAc,CAACpB,OAAOE,SAAS,EAAEL,QAAQE;YAClD,OAAO;gBACL,OAAO,AAACC,OAAOE,SAAS,AAA4B,CAACL,OAAO;YAC9D;QACF;QAEAJ,OAAOW,qBAAqBiB,IAAI,CAAC;QACjC5B,OAAOU,sBAAsBkB,IAAI,CAAC;QAClC5B,OAAOO,OAAOC,wBAAwB,CAACD,OAAOE,SAAS,EAAEL,SAASyB,OAAO,CAACvB;QAC1EN,OAAOY,eAAeiB,OAAO,CAAC;YAC5B,aAAa;gBACXpB,WAAW;oBACT,CAACL,OAAO,EAAE;gBACZ;YACF;YACA0B,SAAS;gBACPX,aAAa;YACf;QACF;QACAnB,OAAOO,OAAOmB,MAAM,CAACd,eAAyB,YAAYgB,IAAI,CAAC;QAC/D5B,OAAOa,eAAegB,OAAO,CAAC;YAC5B,aAAa;gBACXpB,WAAW;oBACT,CAACL,OAAO,EAAE;gBACZ;YACF;YACA2B,UAAU;gBACRC,WAAWC;YACb;QACF;QACAjC,OAAOc,gBAAgBe,OAAO,CAAC;YAC7BC,SAAS;gBACPX,aAAa;gBACba,WAAWC;YACb;QACF;IACF;AACF"}
@@ -0,0 +1,5 @@
1
+ import type { StripeRESTConfig } from '../types.js';
2
+ export declare const sanitizeStripeRESTConfig: ({ rest, }: {
3
+ rest: unknown;
4
+ }) => StripeRESTConfig | undefined;
5
+ //# sourceMappingURL=sanitizeStripeRESTConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizeStripeRESTConfig.d.ts","sourceRoot":"","sources":["../../src/utilities/sanitizeStripeRESTConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAKrE,eAAO,MAAM,wBAAwB,cAElC;IACD,IAAI,EAAE,OAAO,CAAA;CACd,KAAG,gBAAgB,GAAG,SA8BtB,CAAA"}
@@ -0,0 +1,25 @@
1
+ const migrationError = 'The Stripe REST endpoint now requires an object with a non-empty allowedMethods array of exact method names.';
2
+ export const sanitizeStripeRESTConfig = ({ rest })=>{
3
+ if (rest === undefined || rest === false) {
4
+ return undefined;
5
+ }
6
+ if (typeof rest !== 'object' || rest === null || Array.isArray(rest) || !('allowedMethods' in rest) || !Array.isArray(rest.allowedMethods)) {
7
+ throw new Error(migrationError);
8
+ }
9
+ const access = 'access' in rest ? rest.access : undefined;
10
+ if (rest.allowedMethods.length === 0 || rest.allowedMethods.some((method)=>typeof method !== 'string' || method.trim().length === 0 || method.includes('*')) || access !== undefined && !isStripeRESTAccess(access)) {
11
+ throw new Error(migrationError);
12
+ }
13
+ const allowedMethods = Object.freeze([
14
+ ...new Set(rest.allowedMethods)
15
+ ]);
16
+ return access === undefined ? {
17
+ allowedMethods
18
+ } : {
19
+ access,
20
+ allowedMethods
21
+ };
22
+ };
23
+ const isStripeRESTAccess = (access)=>typeof access === 'function';
24
+
25
+ //# sourceMappingURL=sanitizeStripeRESTConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/sanitizeStripeRESTConfig.ts"],"sourcesContent":["import type { StripeRESTAccess, StripeRESTConfig } from '../types.js'\n\nconst migrationError =\n 'The Stripe REST endpoint now requires an object with a non-empty allowedMethods array of exact method names.'\n\nexport const sanitizeStripeRESTConfig = ({\n rest,\n}: {\n rest: unknown\n}): StripeRESTConfig | undefined => {\n if (rest === undefined || rest === false) {\n return undefined\n }\n\n if (\n typeof rest !== 'object' ||\n rest === null ||\n Array.isArray(rest) ||\n !('allowedMethods' in rest) ||\n !Array.isArray(rest.allowedMethods)\n ) {\n throw new Error(migrationError)\n }\n\n const access = 'access' in rest ? rest.access : undefined\n\n if (\n rest.allowedMethods.length === 0 ||\n rest.allowedMethods.some(\n (method) => typeof method !== 'string' || method.trim().length === 0 || method.includes('*'),\n ) ||\n (access !== undefined && !isStripeRESTAccess(access))\n ) {\n throw new Error(migrationError)\n }\n\n const allowedMethods = Object.freeze([...new Set(rest.allowedMethods)])\n\n return access === undefined ? { allowedMethods } : { access, allowedMethods }\n}\n\nconst isStripeRESTAccess = (access: unknown): access is StripeRESTAccess =>\n typeof access === 'function'\n"],"names":["migrationError","sanitizeStripeRESTConfig","rest","undefined","Array","isArray","allowedMethods","Error","access","length","some","method","trim","includes","isStripeRESTAccess","Object","freeze","Set"],"mappings":"AAEA,MAAMA,iBACJ;AAEF,OAAO,MAAMC,2BAA2B,CAAC,EACvCC,IAAI,EAGL;IACC,IAAIA,SAASC,aAAaD,SAAS,OAAO;QACxC,OAAOC;IACT;IAEA,IACE,OAAOD,SAAS,YAChBA,SAAS,QACTE,MAAMC,OAAO,CAACH,SACd,CAAE,CAAA,oBAAoBA,IAAG,KACzB,CAACE,MAAMC,OAAO,CAACH,KAAKI,cAAc,GAClC;QACA,MAAM,IAAIC,MAAMP;IAClB;IAEA,MAAMQ,SAAS,YAAYN,OAAOA,KAAKM,MAAM,GAAGL;IAEhD,IACED,KAAKI,cAAc,CAACG,MAAM,KAAK,KAC/BP,KAAKI,cAAc,CAACI,IAAI,CACtB,CAACC,SAAW,OAAOA,WAAW,YAAYA,OAAOC,IAAI,GAAGH,MAAM,KAAK,KAAKE,OAAOE,QAAQ,CAAC,SAEzFL,WAAWL,aAAa,CAACW,mBAAmBN,SAC7C;QACA,MAAM,IAAID,MAAMP;IAClB;IAEA,MAAMM,iBAAiBS,OAAOC,MAAM,CAAC;WAAI,IAAIC,IAAIf,KAAKI,cAAc;KAAE;IAEtE,OAAOE,WAAWL,YAAY;QAAEG;IAAe,IAAI;QAAEE;QAAQF;IAAe;AAC9E,EAAC;AAED,MAAMQ,qBAAqB,CAACN,SAC1B,OAAOA,WAAW"}