@shopify/ui-extensions-tester 2026.7.0-rc.3 → 2026.7.0-rc.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.
package/README.md CHANGED
@@ -401,6 +401,8 @@ Imports and executes the extension module's default export, rendering the extens
401
401
 
402
402
  A mock `shopify` global, typed correctly for the target under test. You can mutate any property.
403
403
 
404
+ When testing `admin.app.home.render`, the mock `shopify` object also includes `toast`, `app`, and `loading()`.
405
+
404
406
  When testing `admin.app.intent.render`, the mock `shopify.intents` object also includes `response.ok()`, `response.error()`, and `response.closed()`.
405
407
 
406
408
  #### `extension.fetch`
@@ -77,6 +77,28 @@ function createMockStandardRenderingApi(target) {
77
77
  })
78
78
  };
79
79
  }
80
+ function createMockToastApi() {
81
+ return {
82
+ show: () => {},
83
+ hide: () => {}
84
+ };
85
+ }
86
+ function createMockAppApi() {
87
+ return {
88
+ extensions: async () => []
89
+ };
90
+ }
91
+ function createMockLoadingApi() {
92
+ return () => {};
93
+ }
94
+ function createAppHomeMock(target) {
95
+ return {
96
+ ...createMockStandardRenderingApi(target),
97
+ toast: createMockToastApi(),
98
+ app: createMockAppApi(),
99
+ loading: createMockLoadingApi()
100
+ };
101
+ }
80
102
  function createAppIntentRenderMock(target) {
81
103
  return {
82
104
  ...createMockStandardRenderingApi(target),
@@ -231,7 +253,7 @@ const adminMockFactories = {
231
253
  'admin.customers.segmentation-templates.data': createCustomerSegmentTemplateMock,
232
254
  'admin.app.tools.data': createMockStandardApi,
233
255
  // App render targets
234
- 'admin.app.home.render': createMockStandardRenderingApi,
256
+ 'admin.app.home.render': createAppHomeMock,
235
257
  'admin.app.intent.render': createAppIntentRenderMock,
236
258
  // Block targets
237
259
  'admin.product-details.block.render': createMockBlockApi,
@@ -5,14 +5,14 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  /**
6
6
  * The API version supported by this version of the library.
7
7
  *
8
- * At build time, `"2026.7.0-rc.3"` is replaced by rollup with the
8
+ * At build time, `"2026.7.0-rc.5"` is replaced by rollup with the
9
9
  * raw NPM version string from package.json (e.g. `"2026.4.0-rc.1"`).
10
10
  *
11
11
  * When running from source (e.g. in tests), the placeholder is still
12
12
  * present, so we fall back to reading package.json via require.
13
13
  */
14
14
 
15
- const npmVersion = "2026.7.0-rc.3";
15
+ const npmVersion = "2026.7.0-rc.5";
16
16
  function npmVersionToApiVersion(version) {
17
17
  const [year, minor] = version.split('.');
18
18
  return `${year}-${minor.padStart(2, '0')}`;
@@ -77,6 +77,9 @@ var _fetchImpl = /*#__PURE__*/_rollupPluginBabelHelpers.classPrivateFieldLooseKe
77
77
  var _previousFetch = /*#__PURE__*/_rollupPluginBabelHelpers.classPrivateFieldLooseKey("previousFetch");
78
78
  var _navigationImpl = /*#__PURE__*/_rollupPluginBabelHelpers.classPrivateFieldLooseKey("navigationImpl");
79
79
  var _previousNavigation = /*#__PURE__*/_rollupPluginBabelHelpers.classPrivateFieldLooseKey("previousNavigation");
80
+ var _eventListeners = /*#__PURE__*/_rollupPluginBabelHelpers.classPrivateFieldLooseKey("eventListeners");
81
+ var _addEventListener = /*#__PURE__*/_rollupPluginBabelHelpers.classPrivateFieldLooseKey("addEventListener");
82
+ var _removeEventListener = /*#__PURE__*/_rollupPluginBabelHelpers.classPrivateFieldLooseKey("removeEventListener");
80
83
  class Extension {
81
84
  constructor(target, options) {
82
85
  var _options$configSearch;
@@ -120,6 +123,28 @@ class Extension {
120
123
  writable: true,
121
124
  value: void 0
122
125
  });
126
+ Object.defineProperty(this, _eventListeners, {
127
+ writable: true,
128
+ value: new Map()
129
+ });
130
+ Object.defineProperty(this, _addEventListener, {
131
+ writable: true,
132
+ value: (type, listener) => {
133
+ let set = _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type);
134
+ if (!set) {
135
+ set = new Set();
136
+ _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].set(type, set);
137
+ }
138
+ set.add(listener);
139
+ }
140
+ });
141
+ Object.defineProperty(this, _removeEventListener, {
142
+ writable: true,
143
+ value: (type, listener) => {
144
+ var _classPrivateFieldLoo;
145
+ (_classPrivateFieldLoo = _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type)) === null || _classPrivateFieldLoo === void 0 ? void 0 : _classPrivateFieldLoo.delete(listener);
146
+ }
147
+ });
123
148
  const configSearchDir = (_options$configSearch = options === null || options === void 0 ? void 0 : options.configSearchDir) !== null && _options$configSearch !== void 0 ? _options$configSearch : path__namespace.dirname(getCallerFile());
124
149
  const tomlPath = findToml(configSearchDir);
125
150
  const tomlDir = path__namespace.dirname(tomlPath);
@@ -143,10 +168,28 @@ class Extension {
143
168
  _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _previousFetch)[_previousFetch] = globalThis.fetch;
144
169
  _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _previousNavigation)[_previousNavigation] = globalThis.navigation;
145
170
  _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _navigationImpl)[_navigationImpl] = navigation.createMockNavigation();
146
- globalThis.shopify = deepWritableProxy(targetApis.createMockTargetApi(_rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _target)[_target]));
171
+ _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].clear();
172
+ globalThis.shopify = deepWritableProxy(Object.assign(targetApis.createMockTargetApi(_rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _target)[_target]), {
173
+ addEventListener: _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _addEventListener)[_addEventListener],
174
+ removeEventListener: _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _removeEventListener)[_removeEventListener]
175
+ }));
147
176
  globalThis.fetch = _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _fetchImpl)[_fetchImpl];
148
177
  globalThis.navigation = _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _navigationImpl)[_navigationImpl];
149
178
  }
179
+ dispatch(type, event) {
180
+ const listeners = _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type);
181
+ if (!listeners) return;
182
+ // Snapshot so listeners that register/unregister during dispatch
183
+ // don't mutate the iteration.
184
+ for (const listener of [...listeners]) {
185
+ try {
186
+ listener(event);
187
+ } catch {
188
+ // Fire-and-forget: per the shopify.addEventListener contract,
189
+ // listener errors must not affect other listeners.
190
+ }
191
+ }
192
+ }
150
193
  get shopify() {
151
194
  if (!globalThis.shopify) {
152
195
  throw new Error('You must call extension.setUp() before accessing extension.shopify.');
@@ -189,6 +232,7 @@ class Extension {
189
232
  document.body.innerHTML = '';
190
233
  }
191
234
  delete globalThis.shopify;
235
+ _rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].clear();
192
236
  if (_rollupPluginBabelHelpers.classPrivateFieldLooseBase(this, _previousFetch)[_previousFetch] === undefined) {
193
237
  delete globalThis.fetch;
194
238
  } else {
@@ -406,6 +406,58 @@ function createActionTargetCashDrawerMock(target) {
406
406
  };
407
407
  }
408
408
 
409
+ // Data target factories
410
+ function createDataTargetMock(target) {
411
+ return {
412
+ extensionPoint: target,
413
+ extension: {
414
+ apiVersion: '2026-04',
415
+ target
416
+ },
417
+ i18n: i18n.createMockI18n(),
418
+ session: {
419
+ currentSession: createSessionCurrentSession(),
420
+ getSessionToken: async () => 'mock-session-token',
421
+ deviceId: 1
422
+ },
423
+ storage: index.createStorage(),
424
+ locale: {
425
+ current: signals.createReadonlySignalLike('en-US')
426
+ },
427
+ connectivity: {
428
+ current: signals.createReadonlySignalLike(createConnectivityState())
429
+ },
430
+ device: {
431
+ name: 'Mock POS Device',
432
+ registerName: 'Register 1',
433
+ getDeviceId: async () => 'mock-device-id',
434
+ isTablet: async () => false
435
+ },
436
+ productSearch: {
437
+ searchProducts: async () => ({
438
+ items: [],
439
+ hasNextPage: false
440
+ }),
441
+ fetchProductWithId: async () => undefined,
442
+ fetchProductsWithIds: async () => ({
443
+ fetchedResources: [],
444
+ idsForResourcesNotFound: []
445
+ }),
446
+ fetchProductVariantWithId: async () => undefined,
447
+ fetchProductVariantsWithIds: async () => ({
448
+ fetchedResources: [],
449
+ idsForResourcesNotFound: []
450
+ }),
451
+ fetchProductVariantsWithProductId: async () => [],
452
+ fetchPaginatedProductVariantsWithProductId: async () => ({
453
+ items: [],
454
+ hasNextPage: false
455
+ })
456
+ },
457
+ ...createMockCartApi()
458
+ };
459
+ }
460
+
409
461
  // Event target factories
410
462
  function createTransactionCompleteMock(_target) {
411
463
  return {
@@ -491,6 +543,8 @@ const posMockFactories = {
491
543
  'pos.register-details.block.render': createStandardActionCashDrawerMock,
492
544
  // Group Q: ActionTargetApi + CashDrawerApi
493
545
  'pos.register-details.action.render': createActionTargetCashDrawerMock,
546
+ // Data targets
547
+ 'pos.app.ready.data': createDataTargetMock,
494
548
  // Event targets
495
549
  'pos.transaction-complete.event.observe': createTransactionCompleteMock,
496
550
  'pos.cash-tracking-session-start.event.observe': createCashTrackingSessionStartMock,
@@ -17,6 +17,14 @@ Object.defineProperty(exports, '__esModule', { value: true });
17
17
  * For event targets (functions) this is the `data` parameter type.
18
18
  */
19
19
 
20
+ /**
21
+ * Maps an extension target to the event map available via
22
+ * `shopify.addEventListener` on that surface.
23
+ *
24
+ * - POS targets: the POS `ShopifyEventMap`.
25
+ * - Other surfaces: no host events, so the map is empty.
26
+ */
27
+
20
28
  function isCheckoutTarget(target) {
21
29
  return target.startsWith('purchase.checkout') || target.startsWith('purchase.thank-you') || target.startsWith('purchase.cart-line-item') || target.startsWith('Checkout::');
22
30
  }
@@ -73,6 +73,28 @@ function createMockStandardRenderingApi(target) {
73
73
  })
74
74
  };
75
75
  }
76
+ function createMockToastApi() {
77
+ return {
78
+ show: () => {},
79
+ hide: () => {}
80
+ };
81
+ }
82
+ function createMockAppApi() {
83
+ return {
84
+ extensions: async () => []
85
+ };
86
+ }
87
+ function createMockLoadingApi() {
88
+ return () => {};
89
+ }
90
+ function createAppHomeMock(target) {
91
+ return {
92
+ ...createMockStandardRenderingApi(target),
93
+ toast: createMockToastApi(),
94
+ app: createMockAppApi(),
95
+ loading: createMockLoadingApi()
96
+ };
97
+ }
76
98
  function createAppIntentRenderMock(target) {
77
99
  return {
78
100
  ...createMockStandardRenderingApi(target),
@@ -227,7 +249,7 @@ const adminMockFactories = {
227
249
  'admin.customers.segmentation-templates.data': createCustomerSegmentTemplateMock,
228
250
  'admin.app.tools.data': createMockStandardApi,
229
251
  // App render targets
230
- 'admin.app.home.render': createMockStandardRenderingApi,
252
+ 'admin.app.home.render': createAppHomeMock,
231
253
  'admin.app.intent.render': createAppIntentRenderMock,
232
254
  // Block targets
233
255
  'admin.product-details.block.render': createMockBlockApi,
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * The API version supported by this version of the library.
3
3
  *
4
- * At build time, `"2026.7.0-rc.3"` is replaced by rollup with the
4
+ * At build time, `"2026.7.0-rc.5"` is replaced by rollup with the
5
5
  * raw NPM version string from package.json (e.g. `"2026.4.0-rc.1"`).
6
6
  *
7
7
  * When running from source (e.g. in tests), the placeholder is still
8
8
  * present, so we fall back to reading package.json via require.
9
9
  */
10
10
 
11
- const npmVersion = "2026.7.0-rc.3";
11
+ const npmVersion = "2026.7.0-rc.5";
12
12
  function npmVersionToApiVersion(version) {
13
13
  const [year, minor] = version.split('.');
14
14
  return `${year}-${minor.padStart(2, '0')}`;
@@ -53,6 +53,9 @@ var _fetchImpl = /*#__PURE__*/_classPrivateFieldLooseKey("fetchImpl");
53
53
  var _previousFetch = /*#__PURE__*/_classPrivateFieldLooseKey("previousFetch");
54
54
  var _navigationImpl = /*#__PURE__*/_classPrivateFieldLooseKey("navigationImpl");
55
55
  var _previousNavigation = /*#__PURE__*/_classPrivateFieldLooseKey("previousNavigation");
56
+ var _eventListeners = /*#__PURE__*/_classPrivateFieldLooseKey("eventListeners");
57
+ var _addEventListener = /*#__PURE__*/_classPrivateFieldLooseKey("addEventListener");
58
+ var _removeEventListener = /*#__PURE__*/_classPrivateFieldLooseKey("removeEventListener");
56
59
  class Extension {
57
60
  constructor(target, options) {
58
61
  var _options$configSearch;
@@ -96,6 +99,28 @@ class Extension {
96
99
  writable: true,
97
100
  value: void 0
98
101
  });
102
+ Object.defineProperty(this, _eventListeners, {
103
+ writable: true,
104
+ value: new Map()
105
+ });
106
+ Object.defineProperty(this, _addEventListener, {
107
+ writable: true,
108
+ value: (type, listener) => {
109
+ let set = _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type);
110
+ if (!set) {
111
+ set = new Set();
112
+ _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].set(type, set);
113
+ }
114
+ set.add(listener);
115
+ }
116
+ });
117
+ Object.defineProperty(this, _removeEventListener, {
118
+ writable: true,
119
+ value: (type, listener) => {
120
+ var _classPrivateFieldLoo;
121
+ (_classPrivateFieldLoo = _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type)) === null || _classPrivateFieldLoo === void 0 ? void 0 : _classPrivateFieldLoo.delete(listener);
122
+ }
123
+ });
99
124
  const configSearchDir = (_options$configSearch = options === null || options === void 0 ? void 0 : options.configSearchDir) !== null && _options$configSearch !== void 0 ? _options$configSearch : path.dirname(getCallerFile());
100
125
  const tomlPath = findToml(configSearchDir);
101
126
  const tomlDir = path.dirname(tomlPath);
@@ -119,10 +144,28 @@ class Extension {
119
144
  _classPrivateFieldLooseBase(this, _previousFetch)[_previousFetch] = globalThis.fetch;
120
145
  _classPrivateFieldLooseBase(this, _previousNavigation)[_previousNavigation] = globalThis.navigation;
121
146
  _classPrivateFieldLooseBase(this, _navigationImpl)[_navigationImpl] = createMockNavigation();
122
- globalThis.shopify = deepWritableProxy(createMockTargetApi(_classPrivateFieldLooseBase(this, _target)[_target]));
147
+ _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].clear();
148
+ globalThis.shopify = deepWritableProxy(Object.assign(createMockTargetApi(_classPrivateFieldLooseBase(this, _target)[_target]), {
149
+ addEventListener: _classPrivateFieldLooseBase(this, _addEventListener)[_addEventListener],
150
+ removeEventListener: _classPrivateFieldLooseBase(this, _removeEventListener)[_removeEventListener]
151
+ }));
123
152
  globalThis.fetch = _classPrivateFieldLooseBase(this, _fetchImpl)[_fetchImpl];
124
153
  globalThis.navigation = _classPrivateFieldLooseBase(this, _navigationImpl)[_navigationImpl];
125
154
  }
155
+ dispatch(type, event) {
156
+ const listeners = _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type);
157
+ if (!listeners) return;
158
+ // Snapshot so listeners that register/unregister during dispatch
159
+ // don't mutate the iteration.
160
+ for (const listener of [...listeners]) {
161
+ try {
162
+ listener(event);
163
+ } catch {
164
+ // Fire-and-forget: per the shopify.addEventListener contract,
165
+ // listener errors must not affect other listeners.
166
+ }
167
+ }
168
+ }
126
169
  get shopify() {
127
170
  if (!globalThis.shopify) {
128
171
  throw new Error('You must call extension.setUp() before accessing extension.shopify.');
@@ -165,6 +208,7 @@ class Extension {
165
208
  document.body.innerHTML = '';
166
209
  }
167
210
  delete globalThis.shopify;
211
+ _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].clear();
168
212
  if (_classPrivateFieldLooseBase(this, _previousFetch)[_previousFetch] === undefined) {
169
213
  delete globalThis.fetch;
170
214
  } else {
@@ -402,6 +402,58 @@ function createActionTargetCashDrawerMock(target) {
402
402
  };
403
403
  }
404
404
 
405
+ // Data target factories
406
+ function createDataTargetMock(target) {
407
+ return {
408
+ extensionPoint: target,
409
+ extension: {
410
+ apiVersion: '2026-04',
411
+ target
412
+ },
413
+ i18n: createMockI18n(),
414
+ session: {
415
+ currentSession: createSessionCurrentSession(),
416
+ getSessionToken: async () => 'mock-session-token',
417
+ deviceId: 1
418
+ },
419
+ storage: createStorage(),
420
+ locale: {
421
+ current: createReadonlySignalLike('en-US')
422
+ },
423
+ connectivity: {
424
+ current: createReadonlySignalLike(createConnectivityState())
425
+ },
426
+ device: {
427
+ name: 'Mock POS Device',
428
+ registerName: 'Register 1',
429
+ getDeviceId: async () => 'mock-device-id',
430
+ isTablet: async () => false
431
+ },
432
+ productSearch: {
433
+ searchProducts: async () => ({
434
+ items: [],
435
+ hasNextPage: false
436
+ }),
437
+ fetchProductWithId: async () => undefined,
438
+ fetchProductsWithIds: async () => ({
439
+ fetchedResources: [],
440
+ idsForResourcesNotFound: []
441
+ }),
442
+ fetchProductVariantWithId: async () => undefined,
443
+ fetchProductVariantsWithIds: async () => ({
444
+ fetchedResources: [],
445
+ idsForResourcesNotFound: []
446
+ }),
447
+ fetchProductVariantsWithProductId: async () => [],
448
+ fetchPaginatedProductVariantsWithProductId: async () => ({
449
+ items: [],
450
+ hasNextPage: false
451
+ })
452
+ },
453
+ ...createMockCartApi()
454
+ };
455
+ }
456
+
405
457
  // Event target factories
406
458
  function createTransactionCompleteMock(_target) {
407
459
  return {
@@ -487,6 +539,8 @@ const posMockFactories = {
487
539
  'pos.register-details.block.render': createStandardActionCashDrawerMock,
488
540
  // Group Q: ActionTargetApi + CashDrawerApi
489
541
  'pos.register-details.action.render': createActionTargetCashDrawerMock,
542
+ // Data targets
543
+ 'pos.app.ready.data': createDataTargetMock,
490
544
  // Event targets
491
545
  'pos.transaction-complete.event.observe': createTransactionCompleteMock,
492
546
  'pos.cash-tracking-session-start.event.observe': createCashTrackingSessionStartMock,
@@ -13,6 +13,14 @@
13
13
  * For event targets (functions) this is the `data` parameter type.
14
14
  */
15
15
 
16
+ /**
17
+ * Maps an extension target to the event map available via
18
+ * `shopify.addEventListener` on that surface.
19
+ *
20
+ * - POS targets: the POS `ShopifyEventMap`.
21
+ * - Other surfaces: no host events, so the map is empty.
22
+ */
23
+
16
24
  function isCheckoutTarget(target) {
17
25
  return target.startsWith('purchase.checkout') || target.startsWith('purchase.thank-you') || target.startsWith('purchase.cart-line-item') || target.startsWith('Checkout::');
18
26
  }
@@ -73,6 +73,28 @@ function createMockStandardRenderingApi(target) {
73
73
  })
74
74
  };
75
75
  }
76
+ function createMockToastApi() {
77
+ return {
78
+ show: () => {},
79
+ hide: () => {}
80
+ };
81
+ }
82
+ function createMockAppApi() {
83
+ return {
84
+ extensions: async () => []
85
+ };
86
+ }
87
+ function createMockLoadingApi() {
88
+ return () => {};
89
+ }
90
+ function createAppHomeMock(target) {
91
+ return {
92
+ ...createMockStandardRenderingApi(target),
93
+ toast: createMockToastApi(),
94
+ app: createMockAppApi(),
95
+ loading: createMockLoadingApi()
96
+ };
97
+ }
76
98
  function createAppIntentRenderMock(target) {
77
99
  return {
78
100
  ...createMockStandardRenderingApi(target),
@@ -227,7 +249,7 @@ const adminMockFactories = {
227
249
  'admin.customers.segmentation-templates.data': createCustomerSegmentTemplateMock,
228
250
  'admin.app.tools.data': createMockStandardApi,
229
251
  // App render targets
230
- 'admin.app.home.render': createMockStandardRenderingApi,
252
+ 'admin.app.home.render': createAppHomeMock,
231
253
  'admin.app.intent.render': createAppIntentRenderMock,
232
254
  // Block targets
233
255
  'admin.product-details.block.render': createMockBlockApi,
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * The API version supported by this version of the library.
3
3
  *
4
- * At build time, `"2026.7.0-rc.3"` is replaced by rollup with the
4
+ * At build time, `"2026.7.0-rc.5"` is replaced by rollup with the
5
5
  * raw NPM version string from package.json (e.g. `"2026.4.0-rc.1"`).
6
6
  *
7
7
  * When running from source (e.g. in tests), the placeholder is still
8
8
  * present, so we fall back to reading package.json via require.
9
9
  */
10
10
 
11
- const npmVersion = "2026.7.0-rc.3";
11
+ const npmVersion = "2026.7.0-rc.5";
12
12
  function npmVersionToApiVersion(version) {
13
13
  const [year, minor] = version.split('.');
14
14
  return `${year}-${minor.padStart(2, '0')}`;
@@ -53,6 +53,9 @@ var _fetchImpl = /*#__PURE__*/_classPrivateFieldLooseKey("fetchImpl");
53
53
  var _previousFetch = /*#__PURE__*/_classPrivateFieldLooseKey("previousFetch");
54
54
  var _navigationImpl = /*#__PURE__*/_classPrivateFieldLooseKey("navigationImpl");
55
55
  var _previousNavigation = /*#__PURE__*/_classPrivateFieldLooseKey("previousNavigation");
56
+ var _eventListeners = /*#__PURE__*/_classPrivateFieldLooseKey("eventListeners");
57
+ var _addEventListener = /*#__PURE__*/_classPrivateFieldLooseKey("addEventListener");
58
+ var _removeEventListener = /*#__PURE__*/_classPrivateFieldLooseKey("removeEventListener");
56
59
  class Extension {
57
60
  constructor(target, options) {
58
61
  var _options$configSearch;
@@ -96,6 +99,28 @@ class Extension {
96
99
  writable: true,
97
100
  value: void 0
98
101
  });
102
+ Object.defineProperty(this, _eventListeners, {
103
+ writable: true,
104
+ value: new Map()
105
+ });
106
+ Object.defineProperty(this, _addEventListener, {
107
+ writable: true,
108
+ value: (type, listener) => {
109
+ let set = _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type);
110
+ if (!set) {
111
+ set = new Set();
112
+ _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].set(type, set);
113
+ }
114
+ set.add(listener);
115
+ }
116
+ });
117
+ Object.defineProperty(this, _removeEventListener, {
118
+ writable: true,
119
+ value: (type, listener) => {
120
+ var _classPrivateFieldLoo;
121
+ (_classPrivateFieldLoo = _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type)) === null || _classPrivateFieldLoo === void 0 ? void 0 : _classPrivateFieldLoo.delete(listener);
122
+ }
123
+ });
99
124
  const configSearchDir = (_options$configSearch = options === null || options === void 0 ? void 0 : options.configSearchDir) !== null && _options$configSearch !== void 0 ? _options$configSearch : path.dirname(getCallerFile());
100
125
  const tomlPath = findToml(configSearchDir);
101
126
  const tomlDir = path.dirname(tomlPath);
@@ -119,10 +144,28 @@ class Extension {
119
144
  _classPrivateFieldLooseBase(this, _previousFetch)[_previousFetch] = globalThis.fetch;
120
145
  _classPrivateFieldLooseBase(this, _previousNavigation)[_previousNavigation] = globalThis.navigation;
121
146
  _classPrivateFieldLooseBase(this, _navigationImpl)[_navigationImpl] = createMockNavigation();
122
- globalThis.shopify = deepWritableProxy(createMockTargetApi(_classPrivateFieldLooseBase(this, _target)[_target]));
147
+ _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].clear();
148
+ globalThis.shopify = deepWritableProxy(Object.assign(createMockTargetApi(_classPrivateFieldLooseBase(this, _target)[_target]), {
149
+ addEventListener: _classPrivateFieldLooseBase(this, _addEventListener)[_addEventListener],
150
+ removeEventListener: _classPrivateFieldLooseBase(this, _removeEventListener)[_removeEventListener]
151
+ }));
123
152
  globalThis.fetch = _classPrivateFieldLooseBase(this, _fetchImpl)[_fetchImpl];
124
153
  globalThis.navigation = _classPrivateFieldLooseBase(this, _navigationImpl)[_navigationImpl];
125
154
  }
155
+ dispatch(type, event) {
156
+ const listeners = _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].get(type);
157
+ if (!listeners) return;
158
+ // Snapshot so listeners that register/unregister during dispatch
159
+ // don't mutate the iteration.
160
+ for (const listener of [...listeners]) {
161
+ try {
162
+ listener(event);
163
+ } catch {
164
+ // Fire-and-forget: per the shopify.addEventListener contract,
165
+ // listener errors must not affect other listeners.
166
+ }
167
+ }
168
+ }
126
169
  get shopify() {
127
170
  if (!globalThis.shopify) {
128
171
  throw new Error('You must call extension.setUp() before accessing extension.shopify.');
@@ -165,6 +208,7 @@ class Extension {
165
208
  document.body.innerHTML = '';
166
209
  }
167
210
  delete globalThis.shopify;
211
+ _classPrivateFieldLooseBase(this, _eventListeners)[_eventListeners].clear();
168
212
  if (_classPrivateFieldLooseBase(this, _previousFetch)[_previousFetch] === undefined) {
169
213
  delete globalThis.fetch;
170
214
  } else {
@@ -402,6 +402,58 @@ function createActionTargetCashDrawerMock(target) {
402
402
  };
403
403
  }
404
404
 
405
+ // Data target factories
406
+ function createDataTargetMock(target) {
407
+ return {
408
+ extensionPoint: target,
409
+ extension: {
410
+ apiVersion: '2026-04',
411
+ target
412
+ },
413
+ i18n: createMockI18n(),
414
+ session: {
415
+ currentSession: createSessionCurrentSession(),
416
+ getSessionToken: async () => 'mock-session-token',
417
+ deviceId: 1
418
+ },
419
+ storage: createStorage(),
420
+ locale: {
421
+ current: createReadonlySignalLike('en-US')
422
+ },
423
+ connectivity: {
424
+ current: createReadonlySignalLike(createConnectivityState())
425
+ },
426
+ device: {
427
+ name: 'Mock POS Device',
428
+ registerName: 'Register 1',
429
+ getDeviceId: async () => 'mock-device-id',
430
+ isTablet: async () => false
431
+ },
432
+ productSearch: {
433
+ searchProducts: async () => ({
434
+ items: [],
435
+ hasNextPage: false
436
+ }),
437
+ fetchProductWithId: async () => undefined,
438
+ fetchProductsWithIds: async () => ({
439
+ fetchedResources: [],
440
+ idsForResourcesNotFound: []
441
+ }),
442
+ fetchProductVariantWithId: async () => undefined,
443
+ fetchProductVariantsWithIds: async () => ({
444
+ fetchedResources: [],
445
+ idsForResourcesNotFound: []
446
+ }),
447
+ fetchProductVariantsWithProductId: async () => [],
448
+ fetchPaginatedProductVariantsWithProductId: async () => ({
449
+ items: [],
450
+ hasNextPage: false
451
+ })
452
+ },
453
+ ...createMockCartApi()
454
+ };
455
+ }
456
+
405
457
  // Event target factories
406
458
  function createTransactionCompleteMock(_target) {
407
459
  return {
@@ -487,6 +539,8 @@ const posMockFactories = {
487
539
  'pos.register-details.block.render': createStandardActionCashDrawerMock,
488
540
  // Group Q: ActionTargetApi + CashDrawerApi
489
541
  'pos.register-details.action.render': createActionTargetCashDrawerMock,
542
+ // Data targets
543
+ 'pos.app.ready.data': createDataTargetMock,
490
544
  // Event targets
491
545
  'pos.transaction-complete.event.observe': createTransactionCompleteMock,
492
546
  'pos.cash-tracking-session-start.event.observe': createCashTrackingSessionStartMock,
@@ -13,6 +13,14 @@
13
13
  * For event targets (functions) this is the `data` parameter type.
14
14
  */
15
15
 
16
+ /**
17
+ * Maps an extension target to the event map available via
18
+ * `shopify.addEventListener` on that surface.
19
+ *
20
+ * - POS targets: the POS `ShopifyEventMap`.
21
+ * - Other surfaces: no host events, so the map is empty.
22
+ */
23
+
16
24
  function isCheckoutTarget(target) {
17
25
  return target.startsWith('purchase.checkout') || target.startsWith('purchase.thank-you') || target.startsWith('purchase.cart-line-item') || target.startsWith('Checkout::');
18
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"factories.d.ts","sourceRoot":"","sources":["../../../src/admin/factories.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAMjB,MAAM,8BAA8B,CAAC;AAKtC;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,eAAe,IACxD,gBAAgB,CAAC,CAAC,CAAC,SAAS;IAAC,GAAG,EAAE,MAAM,GAAG,CAAA;CAAC,GACxC,IAAI,CAAC,GAAG,EAAE,KAAK,MAAM,EAAE,CAAC,GACxB,KAAK,CAAC;AAsBZ,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAQ7D;AA6UD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,eAAe,EAChE,MAAM,EAAE,CAAC,GACR,oBAAoB,CAAC,CAAC,CAAC,CASzB"}
1
+ {"version":3,"file":"factories.d.ts","sourceRoot":"","sources":["../../../src/admin/factories.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EASjB,MAAM,8BAA8B,CAAC;AAKtC;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,eAAe,IACxD,gBAAgB,CAAC,CAAC,CAAC,SAAS;IAAC,GAAG,EAAE,MAAM,GAAG,CAAA;CAAC,GACxC,IAAI,CAAC,GAAG,EAAE,KAAK,MAAM,EAAE,CAAC,GACxB,KAAK,CAAC;AAsBZ,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAQ7D;AAuWD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,eAAe,EAChE,MAAM,EAAE,CAAC,GACR,oBAAoB,CAAC,CAAC,CAAC,CASzB"}