@stripe/extensibility-custom-objects 0.7.4

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 (48) hide show
  1. package/LICENSE.md +19 -0
  2. package/README.md +63 -0
  3. package/dist/decorators/action.d.ts +29 -0
  4. package/dist/decorators/action.d.ts.map +1 -0
  5. package/dist/decorators/custom-fields.d.ts +16 -0
  6. package/dist/decorators/custom-fields.d.ts.map +1 -0
  7. package/dist/decorators/custom-object.d.ts +19 -0
  8. package/dist/decorators/custom-object.d.ts.map +1 -0
  9. package/dist/decorators/index.d.ts +14 -0
  10. package/dist/decorators/index.d.ts.map +1 -0
  11. package/dist/extensibility-custom-objects-alpha.d.ts +245 -0
  12. package/dist/extensibility-custom-objects-beta.d.ts +245 -0
  13. package/dist/extensibility-custom-objects-internal.d.ts +680 -0
  14. package/dist/extensibility-custom-objects-public.d.ts +245 -0
  15. package/dist/index.cjs +761 -0
  16. package/dist/index.d.ts +26 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +710 -0
  19. package/dist/runtime/base-object.d.ts +84 -0
  20. package/dist/runtime/base-object.d.ts.map +1 -0
  21. package/dist/runtime/deep-clone.d.ts +16 -0
  22. package/dist/runtime/deep-clone.d.ts.map +1 -0
  23. package/dist/runtime/events.d.ts +161 -0
  24. package/dist/runtime/events.d.ts.map +1 -0
  25. package/dist/runtime/flush-helpers.d.ts +9 -0
  26. package/dist/runtime/flush-helpers.d.ts.map +1 -0
  27. package/dist/runtime/invoke.d.ts +19 -0
  28. package/dist/runtime/invoke.d.ts.map +1 -0
  29. package/dist/runtime/persist.d.ts +44 -0
  30. package/dist/runtime/persist.d.ts.map +1 -0
  31. package/dist/runtime/proxy/constants.d.ts +5 -0
  32. package/dist/runtime/proxy/constants.d.ts.map +1 -0
  33. package/dist/runtime/proxy/fields-proxy.d.ts +19 -0
  34. package/dist/runtime/proxy/fields-proxy.d.ts.map +1 -0
  35. package/dist/runtime/proxy/index.d.ts +5 -0
  36. package/dist/runtime/proxy/index.d.ts.map +1 -0
  37. package/dist/runtime/proxy/instance-proxy.d.ts +81 -0
  38. package/dist/runtime/proxy/instance-proxy.d.ts.map +1 -0
  39. package/dist/runtime/proxy/types.d.ts +25 -0
  40. package/dist/runtime/proxy/types.d.ts.map +1 -0
  41. package/dist/runtime/registry.d.ts +40 -0
  42. package/dist/runtime/registry.d.ts.map +1 -0
  43. package/dist/runtime/symbol-helpers.d.ts +17 -0
  44. package/dist/runtime/symbol-helpers.d.ts.map +1 -0
  45. package/dist/runtime/types.d.ts +139 -0
  46. package/dist/runtime/types.d.ts.map +1 -0
  47. package/dist/tsconfig.build.tsbuildinfo +1 -0
  48. package/package.json +48 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,761 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ Action: () => Action,
24
+ BaseObject: () => BaseObject,
25
+ CustomFields: () => CustomFields,
26
+ CustomObject: () => CustomObject,
27
+ _INSTANCE_CONTEXT: () => _INSTANCE_CONTEXT,
28
+ _PROXY_STATE: () => _PROXY_STATE,
29
+ _UNTRACKED_FIELDS: () => _UNTRACKED_FIELDS,
30
+ __hasPendingChanges: () => __hasPendingChanges,
31
+ __queueSave: () => __queueSave,
32
+ _assemblePayload: () => _assemblePayload,
33
+ _buildMethodResponse: () => _buildMethodResponse,
34
+ _clearRegistry: () => _clearRegistry,
35
+ _clearSlatedUpdates: () => _clearSlatedUpdates,
36
+ _commitProxyState: () => _commitProxyState,
37
+ _createEventEmitter: () => _createEventEmitter,
38
+ _createInstanceProxy: () => _createInstanceProxy,
39
+ _executeMethod: () => _executeMethod,
40
+ _getProxyState: () => _getProxyState,
41
+ _getRegisteredApiNames: () => _getRegisteredApiNames,
42
+ _getRegisteredClass: () => _getRegisteredClass,
43
+ _getSlatedUpdates: () => _getSlatedUpdates,
44
+ _hasProxyState: () => _hasProxyState,
45
+ _isTrackedField: () => _isTrackedField,
46
+ _persistObject: () => _persistObject,
47
+ _registerClass: () => _registerClass
48
+ });
49
+ module.exports = __toCommonJS(src_exports);
50
+
51
+ // src/decorators/custom-object.ts
52
+ function CustomObject(_target, _context) {
53
+ }
54
+
55
+ // src/decorators/custom-fields.ts
56
+ function CustomFields(_target, _context) {
57
+ }
58
+
59
+ // src/decorators/action.ts
60
+ function Action(_target, _context) {
61
+ }
62
+
63
+ // src/runtime/types.ts
64
+ var _PROXY_STATE = /* @__PURE__ */ Symbol("stripe:co:proxyState");
65
+ var _INSTANCE_CONTEXT = /* @__PURE__ */ Symbol("stripe:co:instanceContext");
66
+ function _hasProxyState(obj) {
67
+ return typeof obj === "object" && obj !== null && _PROXY_STATE in obj && // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- symbol-keyed property access requires casting; there is no type-safe alternative for inspecting symbol properties on an `object`
68
+ typeof obj[_PROXY_STATE] === "object" && // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- symbol-keyed property access requires casting; there is no type-safe alternative for inspecting symbol properties on an `object`
69
+ obj[_PROXY_STATE] !== null;
70
+ }
71
+
72
+ // src/runtime/proxy/constants.ts
73
+ var STRIPE_CONTROLLED_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
74
+ var FIELDS_PREFIX = "fields.";
75
+
76
+ // src/runtime/symbol-helpers.ts
77
+ function getSymbolProp(obj, sym) {
78
+ return obj[sym];
79
+ }
80
+ function setSymbolProp(obj, sym, value) {
81
+ obj[sym] = value;
82
+ }
83
+
84
+ // src/runtime/flush-helpers.ts
85
+ function _flushPendingToSlated(state) {
86
+ const flushed = /* @__PURE__ */ new Set();
87
+ for (const update of state.pendingUpdates) {
88
+ const key = update.field.startsWith(FIELDS_PREFIX) ? update.field.slice(FIELDS_PREFIX.length) : update.field;
89
+ state.slatedUpdates[key] = update.value;
90
+ flushed.add(key);
91
+ }
92
+ state.pendingUpdates = [];
93
+ return flushed.size;
94
+ }
95
+
96
+ // src/runtime/deep-clone.ts
97
+ function deepClone(value) {
98
+ if (typeof globalThis.structuredClone === "function") {
99
+ return globalThis.structuredClone(value);
100
+ }
101
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
102
+ return value;
103
+ }
104
+ return JSON.parse(JSON.stringify(value));
105
+ }
106
+
107
+ // src/runtime/base-object.ts
108
+ var BaseObject = class {
109
+ /** Unique identifier assigned by the platform */
110
+ id;
111
+ /** Object type identifier assigned by the platform */
112
+ object;
113
+ /** Timestamp when the object was created */
114
+ created;
115
+ /** Timestamp when the object was last updated */
116
+ updated;
117
+ /** Whether the object exists in live mode (true) or test mode (false). @public */
118
+ livemode;
119
+ /**
120
+ * User-defined fields container. Populated during hydration via Object.assign
121
+ * before the dirty-tracking proxy is installed.
122
+ * Access individual fields as `this.fields.fieldName`.
123
+ *
124
+ * @remarks
125
+ * The generic type `T` describes the shape of available fields after
126
+ * hydration, not at construction time. Fields may be undefined before
127
+ * the instance is hydrated with data from the platform.
128
+ *
129
+ * @public
130
+ */
131
+ fields;
132
+ // No user-facing constructor — hydration is performed by the runtime
133
+ // via Object.assign before the dirty-tracking proxy is installed.
134
+ /**
135
+ * Flushes pending field mutations into the slated-updates accumulator.
136
+ *
137
+ * When called from a transformed action (no save handler configured), this
138
+ * records which fields were mutated so that the Logic wrapper can include them
139
+ * in the response envelope's `updatedFields`. No network call is made.
140
+ *
141
+ * When a save handler is configured (runtime invoke path), the handler is
142
+ * called for actual persistence and its result is returned.
143
+ *
144
+ * @returns The save result including `updatedFieldCount` (unique fields flushed)
145
+ * @throws Error if the object is not proxied
146
+ * @public
147
+ */
148
+ async save() {
149
+ const context = getSymbolProp(this, _INSTANCE_CONTEXT);
150
+ if (!context) {
151
+ throw new Error("Cannot save: object is not proxied");
152
+ }
153
+ const { state } = context;
154
+ const updatedFieldCount = _flushPendingToSlated(state);
155
+ if (context.saveHandler) {
156
+ await context.saveHandler(this, state, context.eventEmitter);
157
+ }
158
+ state.lastSavedSnapshot = Object.fromEntries(
159
+ Object.entries(state.fields).map(([k, v]) => [k, deepClone(v)])
160
+ );
161
+ return { updatedFieldCount };
162
+ }
163
+ /**
164
+ * Discard all pending changes and restore field values to their
165
+ * last-saved state. Has no effect if the object is not proxied.
166
+ *
167
+ * @public
168
+ */
169
+ revert() {
170
+ const context = getSymbolProp(this, _INSTANCE_CONTEXT);
171
+ const state = context?.state;
172
+ if (state) {
173
+ for (const [key, value] of Object.entries(state.lastSavedSnapshot)) {
174
+ if (key.startsWith(FIELDS_PREFIX)) {
175
+ const fieldName = key.slice(FIELDS_PREFIX.length);
176
+ this.fields[fieldName] = deepClone(value);
177
+ } else {
178
+ this[key] = deepClone(value);
179
+ }
180
+ }
181
+ for (const key of Object.keys(state.fields)) {
182
+ if (!(key in state.lastSavedSnapshot)) {
183
+ if (key.startsWith(FIELDS_PREFIX)) {
184
+ const fieldName = key.slice(FIELDS_PREFIX.length);
185
+ Reflect.deleteProperty(this.fields, fieldName);
186
+ } else {
187
+ throw new Error(`Cannot delete outer class property: ${key}`);
188
+ }
189
+ }
190
+ }
191
+ state.fields = Object.fromEntries(
192
+ Object.entries(state.lastSavedSnapshot).map(([k, v]) => [k, deepClone(v)])
193
+ );
194
+ state.pendingUpdates = [];
195
+ }
196
+ }
197
+ };
198
+ var _UNTRACKED_FIELDS = /* @__PURE__ */ new Set([
199
+ "id",
200
+ "object",
201
+ "created",
202
+ "updated",
203
+ "livemode"
204
+ ]);
205
+ function _isTrackedField(field) {
206
+ return !_UNTRACKED_FIELDS.has(field);
207
+ }
208
+
209
+ // src/runtime/events.ts
210
+ function _createEventEmitter() {
211
+ const listeners = /* @__PURE__ */ new Set();
212
+ return {
213
+ emit(event) {
214
+ for (const listener of listeners) {
215
+ try {
216
+ listener(event);
217
+ } catch (error) {
218
+ console.error("Event listener threw:", error);
219
+ }
220
+ }
221
+ },
222
+ subscribe(listener) {
223
+ listeners.add(listener);
224
+ return () => {
225
+ listeners.delete(listener);
226
+ };
227
+ }
228
+ };
229
+ }
230
+
231
+ // src/runtime/proxy/fields-proxy.ts
232
+ function createFieldsProxy(target, state, getEventEmitter) {
233
+ const fieldProxyCaches = /* @__PURE__ */ new Map();
234
+ function getOrCreateCache(fieldKey) {
235
+ let cache = fieldProxyCaches.get(fieldKey);
236
+ if (!cache) {
237
+ cache = /* @__PURE__ */ new Map();
238
+ fieldProxyCaches.set(fieldKey, cache);
239
+ }
240
+ return cache;
241
+ }
242
+ function markFieldDirty(fieldKey, rootValue) {
243
+ state.fields[fieldKey] = rootValue;
244
+ if (state.hydrating) {
245
+ return;
246
+ }
247
+ const timestamp = Date.now();
248
+ state.pendingUpdates.push({ field: fieldKey, value: rootValue, timestamp });
249
+ const emitter = getEventEmitter?.();
250
+ if (emitter) {
251
+ emitter.emit({ type: "proxy:set", field: fieldKey, value: rootValue, timestamp });
252
+ }
253
+ }
254
+ function wrapNested(obj, fieldKey, rootValue, cache) {
255
+ const existing = cache.get(obj);
256
+ if (existing) return existing;
257
+ const proxy = new Proxy(obj, {
258
+ get(t, prop, receiver) {
259
+ const value = Reflect.get(t, prop, receiver);
260
+ if (typeof value === "object" && value !== null && typeof prop !== "symbol") {
261
+ const proto = Object.getPrototypeOf(value);
262
+ if (proto === Object.prototype || proto === Array.prototype || proto === null) {
263
+ return wrapNested(value, fieldKey, rootValue, cache);
264
+ }
265
+ }
266
+ return value;
267
+ },
268
+ set(t, prop, value) {
269
+ if (typeof prop === "symbol") {
270
+ throw new Error("Cannot use symbol keys on fields");
271
+ }
272
+ if (STRIPE_CONTROLLED_KEYS.has(prop)) {
273
+ throw new Error(`Cannot set reserved key "${prop}" on fields`);
274
+ }
275
+ const success = Reflect.set(t, prop, value, t);
276
+ if (success) markFieldDirty(fieldKey, rootValue);
277
+ return success;
278
+ },
279
+ deleteProperty(t, prop) {
280
+ if (typeof prop === "symbol") {
281
+ throw new Error("Cannot use symbol keys on fields");
282
+ }
283
+ if (STRIPE_CONTROLLED_KEYS.has(prop)) {
284
+ throw new Error(`Cannot delete reserved key "${prop}" on fields`);
285
+ }
286
+ const success = Reflect.deleteProperty(t, prop);
287
+ if (success) markFieldDirty(fieldKey, rootValue);
288
+ return success;
289
+ },
290
+ defineProperty(t, prop, attributes) {
291
+ const success = Reflect.defineProperty(t, prop, attributes);
292
+ if (success && typeof prop !== "symbol") markFieldDirty(fieldKey, rootValue);
293
+ return success;
294
+ }
295
+ });
296
+ cache.set(obj, proxy);
297
+ return proxy;
298
+ }
299
+ return new Proxy(target, {
300
+ get(obj, prop, receiver) {
301
+ if (typeof prop === "symbol") {
302
+ return Reflect.get(obj, prop, receiver);
303
+ }
304
+ const value = Reflect.get(obj, prop, receiver);
305
+ if (typeof value === "object" && value !== null) {
306
+ const proto = Object.getPrototypeOf(value);
307
+ if (proto === Object.prototype || proto === Array.prototype || proto === null) {
308
+ const fieldKey = `${FIELDS_PREFIX}${prop}`;
309
+ const cache = getOrCreateCache(fieldKey);
310
+ return wrapNested(value, fieldKey, value, cache);
311
+ }
312
+ }
313
+ return value;
314
+ },
315
+ set(obj, prop, value) {
316
+ if (typeof prop === "symbol") {
317
+ throw new Error("Cannot use symbol keys on fields");
318
+ }
319
+ if (STRIPE_CONTROLLED_KEYS.has(prop)) {
320
+ throw new Error(`Cannot set reserved key "${prop}" on fields`);
321
+ }
322
+ const success = Reflect.set(obj, prop, value, obj);
323
+ if (success) {
324
+ markFieldDirty(`${FIELDS_PREFIX}${prop}`, value);
325
+ }
326
+ return success;
327
+ },
328
+ deleteProperty(obj, prop) {
329
+ if (typeof prop === "symbol") {
330
+ throw new Error("Cannot use symbol keys on fields");
331
+ }
332
+ if (STRIPE_CONTROLLED_KEYS.has(prop)) {
333
+ throw new Error(`Cannot delete reserved key "${prop}" on fields`);
334
+ }
335
+ const success = Reflect.deleteProperty(obj, prop);
336
+ if (success) {
337
+ markFieldDirty(`${FIELDS_PREFIX}${prop}`, void 0);
338
+ }
339
+ return success;
340
+ }
341
+ });
342
+ }
343
+
344
+ // src/runtime/proxy/instance-proxy.ts
345
+ var PROTECTED_INSTANCE_KEYS = /* @__PURE__ */ new Set([
346
+ // BaseObject methods
347
+ "save",
348
+ "revert",
349
+ // Core Object prototype methods
350
+ "hasOwnProperty",
351
+ "isPrototypeOf",
352
+ "propertyIsEnumerable",
353
+ "toLocaleString",
354
+ "toString",
355
+ "valueOf",
356
+ // Prototype pollution vectors (also guarded on fields proxy via STRIPE_CONTROLLED_KEYS)
357
+ "__proto__",
358
+ "constructor",
359
+ "prototype"
360
+ ]);
361
+ var globalEventEmitter;
362
+ function setGlobalEventEmitter(emitter) {
363
+ globalEventEmitter = emitter;
364
+ }
365
+ function getGlobalEventEmitter() {
366
+ return globalEventEmitter;
367
+ }
368
+ function _createInstanceProxy(instance, options) {
369
+ const state = {
370
+ fields: {},
371
+ pendingUpdates: [],
372
+ lastSavedSnapshot: {},
373
+ hydrating: false,
374
+ slatedUpdates: {}
375
+ };
376
+ const context = {
377
+ state,
378
+ ...options?.eventEmitter !== void 0 && { eventEmitter: options.eventEmitter },
379
+ ...options?.saveHandler !== void 0 && { saveHandler: options.saveHandler }
380
+ };
381
+ setSymbolProp(instance, _INSTANCE_CONTEXT, context);
382
+ const getEventEmitter = () => {
383
+ return context.eventEmitter ?? globalEventEmitter;
384
+ };
385
+ let fieldsProxy;
386
+ const proxy = new Proxy(instance, {
387
+ get(target, prop, receiver) {
388
+ if (prop === _PROXY_STATE) {
389
+ return state;
390
+ }
391
+ if (prop === "fields") {
392
+ if (!fieldsProxy) {
393
+ const fields = Reflect.get(target, prop, receiver);
394
+ fieldsProxy = createFieldsProxy(fields, state, getEventEmitter);
395
+ }
396
+ return fieldsProxy;
397
+ }
398
+ return Reflect.get(target, prop, receiver);
399
+ },
400
+ set(target, prop, value, _receiver) {
401
+ if (typeof prop === "symbol") {
402
+ return Reflect.set(target, prop, value, target);
403
+ }
404
+ if (prop === "fields") {
405
+ throw new Error(
406
+ "Cannot replace the fields object directly. Mutate individual fields instead: this.fields.fieldName = value"
407
+ );
408
+ }
409
+ if (_UNTRACKED_FIELDS.has(prop) && !state.hydrating) {
410
+ throw new Error(
411
+ `Cannot set read-only field "${prop}". Platform fields (id, object, created, updated, livemode) are managed by the runtime.`
412
+ );
413
+ }
414
+ if (PROTECTED_INSTANCE_KEYS.has(prop)) {
415
+ throw new Error(
416
+ `Cannot overwrite "${prop}". This key is reserved by the runtime.`
417
+ );
418
+ }
419
+ const result = Reflect.set(target, prop, value, target);
420
+ if (state.hydrating) {
421
+ return result;
422
+ }
423
+ const timestamp = Date.now();
424
+ state.pendingUpdates.push({ field: prop, value, timestamp });
425
+ state.fields[prop] = value;
426
+ const emitter = getEventEmitter();
427
+ if (emitter) {
428
+ emitter.emit({
429
+ type: "proxy:set",
430
+ field: prop,
431
+ value,
432
+ timestamp
433
+ });
434
+ }
435
+ return result;
436
+ },
437
+ defineProperty(target, prop, descriptor) {
438
+ if (typeof prop === "symbol") {
439
+ return Reflect.defineProperty(target, prop, descriptor);
440
+ }
441
+ if (prop === "fields") {
442
+ throw new Error(
443
+ "Cannot replace the fields object directly. Mutate individual fields instead: this.fields.fieldName = value"
444
+ );
445
+ }
446
+ if (_UNTRACKED_FIELDS.has(prop)) {
447
+ throw new Error(
448
+ `Cannot set read-only field "${prop}". Platform fields (id, object, created, updated, livemode) are managed by the runtime.`
449
+ );
450
+ }
451
+ if (PROTECTED_INSTANCE_KEYS.has(prop)) {
452
+ throw new Error(
453
+ `Cannot overwrite "${prop}". This key is reserved by the runtime.`
454
+ );
455
+ }
456
+ const result = Reflect.defineProperty(target, prop, descriptor);
457
+ if (result) {
458
+ const value = descriptor.value;
459
+ const timestamp = Date.now();
460
+ state.pendingUpdates.push({ field: prop, value, timestamp });
461
+ state.fields[prop] = value;
462
+ const emitter = getEventEmitter();
463
+ if (emitter) {
464
+ emitter.emit({ type: "proxy:set", field: prop, value, timestamp });
465
+ }
466
+ }
467
+ return result;
468
+ }
469
+ });
470
+ return { proxy, state };
471
+ }
472
+ function _getProxyState(obj) {
473
+ return getSymbolProp(obj, _PROXY_STATE);
474
+ }
475
+ function _commitProxyState(state) {
476
+ state.pendingUpdates = [];
477
+ state.lastSavedSnapshot = Object.fromEntries(
478
+ Object.entries(state.fields).map(([k, v]) => [k, deepClone(v)])
479
+ );
480
+ }
481
+ function __hasPendingChanges(obj) {
482
+ const state = _getProxyState(obj);
483
+ return state ? state.pendingUpdates.length > 0 : false;
484
+ }
485
+ function __queueSave(obj) {
486
+ const state = _getProxyState(obj);
487
+ if (!state) {
488
+ throw new Error(
489
+ "__queueSave called on an object without proxy state. This is a bug \u2014 the object must be wrapped via _createInstanceProxy before save."
490
+ );
491
+ }
492
+ if (state.pendingUpdates.length === 0) {
493
+ return;
494
+ }
495
+ _flushPendingToSlated(state);
496
+ }
497
+ function _getSlatedUpdates(obj) {
498
+ const state = _getProxyState(obj);
499
+ return state ? { ...state.slatedUpdates } : {};
500
+ }
501
+ function _clearSlatedUpdates(obj) {
502
+ const state = _getProxyState(obj);
503
+ if (state) {
504
+ state.slatedUpdates = {};
505
+ }
506
+ }
507
+ function _buildMethodResponse(obj, methodReturnValue) {
508
+ const updatedFields = _getSlatedUpdates(obj);
509
+ _clearSlatedUpdates(obj);
510
+ return { methodReturnValue, updatedFields };
511
+ }
512
+
513
+ // src/runtime/registry.ts
514
+ var registry = /* @__PURE__ */ new Map();
515
+ function _registerClass(constructor) {
516
+ if (!constructor.__platformMeta__) {
517
+ throw new Error(
518
+ `Cannot register class ${constructor.name}: missing __platformMeta__ (did you forget the @CustomObject decorator?)`
519
+ );
520
+ }
521
+ const { apiName } = constructor.__platformMeta__;
522
+ registry.set(apiName, constructor);
523
+ }
524
+ function _getRegisteredClass(apiName) {
525
+ return registry.get(apiName);
526
+ }
527
+ function _clearRegistry() {
528
+ registry.clear();
529
+ }
530
+ function _getRegisteredApiNames() {
531
+ return Array.from(registry.keys());
532
+ }
533
+
534
+ // src/runtime/persist.ts
535
+ function _assemblePayload(pendingUpdates) {
536
+ const payload = {};
537
+ const fields = {};
538
+ for (const update of pendingUpdates) {
539
+ const value = update.value === void 0 ? null : update.value;
540
+ if (update.field.startsWith(FIELDS_PREFIX)) {
541
+ const key = update.field.slice(FIELDS_PREFIX.length);
542
+ fields[key] = value;
543
+ } else {
544
+ payload[update.field] = value;
545
+ }
546
+ }
547
+ if (Object.keys(fields).length > 0) {
548
+ payload.fields = fields;
549
+ }
550
+ return payload;
551
+ }
552
+ async function _persistObject(obj, options) {
553
+ const emitter = options?.eventEmitter;
554
+ const state = _getProxyState(obj);
555
+ if (!state) {
556
+ throw new Error("Cannot persist: object is not proxied");
557
+ }
558
+ if (state.pendingUpdates.length === 0) {
559
+ return;
560
+ }
561
+ const payload = _assemblePayload(state.pendingUpdates);
562
+ if (emitter) {
563
+ emitter.emit({ type: "save:begin", payload });
564
+ }
565
+ const fetchImpl = options?.fetchFn ?? fetch;
566
+ const baseUrl = options?.baseUrl ?? "";
567
+ const url = `${baseUrl}/custom-objects/${obj.id}`;
568
+ const response = await fetchImpl(url, {
569
+ method: "PATCH",
570
+ headers: {
571
+ "Content-Type": "application/json"
572
+ },
573
+ body: JSON.stringify(payload)
574
+ });
575
+ if (!response.ok) {
576
+ throw new Error(`Persist failed: ${String(response.status)} ${response.statusText}`);
577
+ }
578
+ const parsed = await response.json();
579
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
580
+ throw new Error("Unexpected response shape from persistence API");
581
+ }
582
+ const result = parsed;
583
+ _commitProxyState(state);
584
+ if (emitter) {
585
+ emitter.emit({ type: "save:complete", response: result });
586
+ }
587
+ }
588
+
589
+ // src/runtime/invoke.ts
590
+ var WIRE_DATA_KEY = "data";
591
+ function sanitizeKeys(obj) {
592
+ const sanitized = {};
593
+ for (const key of Object.keys(obj)) {
594
+ if (!STRIPE_CONTROLLED_KEYS.has(key)) {
595
+ sanitized[key] = obj[key];
596
+ }
597
+ }
598
+ return sanitized;
599
+ }
600
+ function hydrateInstance(instance, data) {
601
+ const record = instance;
602
+ Object.assign(record, data);
603
+ }
604
+ async function _executeMethod(request, options) {
605
+ const emitter = options?.eventEmitter;
606
+ if (emitter) {
607
+ emitter.emit({ type: "invoke:begin", request });
608
+ }
609
+ try {
610
+ const Constructor = _getRegisteredClass(request.apiName);
611
+ if (!Constructor) {
612
+ throw new Error(`Unknown custom object type: ${request.apiName}`);
613
+ }
614
+ if (!request.instanceId) {
615
+ throw new Error(
616
+ `Instance ID is required for method dispatch on: ${request.apiName}`
617
+ );
618
+ }
619
+ const result = await _executeInstanceMethod(
620
+ request,
621
+ Constructor,
622
+ request.instanceId,
623
+ emitter
624
+ );
625
+ if (emitter) {
626
+ emitter.emit({ type: "invoke:complete", result });
627
+ }
628
+ return result;
629
+ } catch (error) {
630
+ if (emitter) {
631
+ emitter.emit({ type: "invoke:error", error });
632
+ }
633
+ throw error;
634
+ }
635
+ }
636
+ async function _executeInstanceMethod(request, Constructor, instanceId, emitter) {
637
+ const instance = new Constructor();
638
+ const instanceRecord = instance;
639
+ instanceRecord.fields = {};
640
+ if (request.instanceData) {
641
+ const { [WIRE_DATA_KEY]: dataFields, ...platformFields } = request.instanceData;
642
+ Object.assign(instanceRecord, sanitizeKeys(platformFields));
643
+ hydrateInstance(instance, { id: instanceId });
644
+ if (dataFields && typeof dataFields === "object" && !Array.isArray(dataFields)) {
645
+ Object.assign(instance.fields, sanitizeKeys(dataFields));
646
+ }
647
+ } else {
648
+ hydrateInstance(instance, { id: instanceId });
649
+ }
650
+ const { proxy, state } = _createInstanceProxy(instance, {
651
+ ...emitter !== void 0 && { eventEmitter: emitter }
652
+ });
653
+ if (request.instanceData) {
654
+ const { [WIRE_DATA_KEY]: dataFields, ...platformFields } = request.instanceData;
655
+ if (dataFields && typeof dataFields === "object" && !Array.isArray(dataFields)) {
656
+ const dataFieldsRecord = dataFields;
657
+ for (const [key, value] of Object.entries(sanitizeKeys(dataFieldsRecord))) {
658
+ state.fields[`${FIELDS_PREFIX}${key}`] = value;
659
+ }
660
+ }
661
+ for (const [key, value] of Object.entries(sanitizeKeys(platformFields))) {
662
+ if (!_UNTRACKED_FIELDS.has(key)) {
663
+ state.fields[key] = value;
664
+ }
665
+ }
666
+ state.lastSavedSnapshot = Object.fromEntries(
667
+ Object.entries(state.fields).map(([k, v]) => [k, deepClone(v)])
668
+ );
669
+ }
670
+ state.pendingUpdates = [];
671
+ const actionMeta = Constructor.__platformMeta__?.actions.instance[request.methodName];
672
+ if (!actionMeta) {
673
+ throw new Error(`Unknown instance method: ${request.apiName}.${request.methodName}`);
674
+ }
675
+ const previousEmitter = getGlobalEventEmitter();
676
+ setGlobalEventEmitter(emitter);
677
+ try {
678
+ const spanId = `${request.apiName}.${request.methodName}`;
679
+ const startTime = Date.now();
680
+ if (emitter) {
681
+ emitter.emit({
682
+ type: "span:begin",
683
+ spanId,
684
+ name: request.methodName,
685
+ params: request.args,
686
+ isStatic: false
687
+ });
688
+ }
689
+ try {
690
+ const method = Reflect.get(proxy, request.methodName);
691
+ if (typeof method !== "function") {
692
+ throw new Error(`Method ${request.methodName} is not a function`);
693
+ }
694
+ const result = await method.apply(
695
+ proxy,
696
+ request.args ?? []
697
+ );
698
+ const duration = Date.now() - startTime;
699
+ if (emitter) {
700
+ emitter.emit({
701
+ type: "span:end",
702
+ spanId,
703
+ name: request.methodName,
704
+ duration,
705
+ result,
706
+ isStatic: false
707
+ });
708
+ }
709
+ return result;
710
+ } catch (error) {
711
+ const duration = Date.now() - startTime;
712
+ if (emitter) {
713
+ emitter.emit({
714
+ type: "span:error",
715
+ spanId,
716
+ name: request.methodName,
717
+ duration,
718
+ error: {
719
+ name: error instanceof Error ? error.name : "Error",
720
+ message: error instanceof Error ? error.message : String(error),
721
+ ...error instanceof Error && error.stack !== void 0 && {
722
+ stack: error.stack
723
+ }
724
+ },
725
+ isStatic: false
726
+ });
727
+ }
728
+ throw error;
729
+ }
730
+ } finally {
731
+ setGlobalEventEmitter(previousEmitter);
732
+ }
733
+ }
734
+ // Annotate the CommonJS export names for ESM import in node:
735
+ 0 && (module.exports = {
736
+ Action,
737
+ BaseObject,
738
+ CustomFields,
739
+ CustomObject,
740
+ _INSTANCE_CONTEXT,
741
+ _PROXY_STATE,
742
+ _UNTRACKED_FIELDS,
743
+ __hasPendingChanges,
744
+ __queueSave,
745
+ _assemblePayload,
746
+ _buildMethodResponse,
747
+ _clearRegistry,
748
+ _clearSlatedUpdates,
749
+ _commitProxyState,
750
+ _createEventEmitter,
751
+ _createInstanceProxy,
752
+ _executeMethod,
753
+ _getProxyState,
754
+ _getRegisteredApiNames,
755
+ _getRegisteredClass,
756
+ _getSlatedUpdates,
757
+ _hasProxyState,
758
+ _isTrackedField,
759
+ _persistObject,
760
+ _registerClass
761
+ });