@uniforge/testing 0.1.0-alpha.2

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 (85) hide show
  1. package/dist/auth/index.d.cts +177 -0
  2. package/dist/auth/index.d.ts +177 -0
  3. package/dist/auth/index.js +459 -0
  4. package/dist/auth/index.js.map +1 -0
  5. package/dist/auth/index.mjs +418 -0
  6. package/dist/auth/index.mjs.map +1 -0
  7. package/dist/billing/index.d.cts +27 -0
  8. package/dist/billing/index.d.ts +27 -0
  9. package/dist/billing/index.js +208 -0
  10. package/dist/billing/index.js.map +1 -0
  11. package/dist/billing/index.mjs +178 -0
  12. package/dist/billing/index.mjs.map +1 -0
  13. package/dist/database/index.d.cts +399 -0
  14. package/dist/database/index.d.ts +399 -0
  15. package/dist/database/index.js +19054 -0
  16. package/dist/database/index.js.map +1 -0
  17. package/dist/database/index.mjs +19046 -0
  18. package/dist/database/index.mjs.map +1 -0
  19. package/dist/graphql/index.d.cts +23 -0
  20. package/dist/graphql/index.d.ts +23 -0
  21. package/dist/graphql/index.js +18511 -0
  22. package/dist/graphql/index.js.map +1 -0
  23. package/dist/graphql/index.mjs +18505 -0
  24. package/dist/graphql/index.mjs.map +1 -0
  25. package/dist/index.d.cts +10 -0
  26. package/dist/index.d.ts +10 -0
  27. package/dist/index.js +31 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/index.mjs +6 -0
  30. package/dist/index.mjs.map +1 -0
  31. package/dist/multi-store/index.d.cts +66 -0
  32. package/dist/multi-store/index.d.ts +66 -0
  33. package/dist/multi-store/index.js +319 -0
  34. package/dist/multi-store/index.js.map +1 -0
  35. package/dist/multi-store/index.mjs +287 -0
  36. package/dist/multi-store/index.mjs.map +1 -0
  37. package/dist/multi-tenant/index.d.cts +15 -0
  38. package/dist/multi-tenant/index.d.ts +15 -0
  39. package/dist/multi-tenant/index.js +87 -0
  40. package/dist/multi-tenant/index.js.map +1 -0
  41. package/dist/multi-tenant/index.mjs +57 -0
  42. package/dist/multi-tenant/index.mjs.map +1 -0
  43. package/dist/performance/index.d.cts +60 -0
  44. package/dist/performance/index.d.ts +60 -0
  45. package/dist/performance/index.js +280 -0
  46. package/dist/performance/index.js.map +1 -0
  47. package/dist/performance/index.mjs +246 -0
  48. package/dist/performance/index.mjs.map +1 -0
  49. package/dist/platform/index.d.cts +71 -0
  50. package/dist/platform/index.d.ts +71 -0
  51. package/dist/platform/index.js +435 -0
  52. package/dist/platform/index.js.map +1 -0
  53. package/dist/platform/index.mjs +396 -0
  54. package/dist/platform/index.mjs.map +1 -0
  55. package/dist/rbac/index.d.cts +21 -0
  56. package/dist/rbac/index.d.ts +21 -0
  57. package/dist/rbac/index.js +178 -0
  58. package/dist/rbac/index.js.map +1 -0
  59. package/dist/rbac/index.mjs +150 -0
  60. package/dist/rbac/index.mjs.map +1 -0
  61. package/dist/security/index.d.cts +73 -0
  62. package/dist/security/index.d.ts +73 -0
  63. package/dist/security/index.js +246 -0
  64. package/dist/security/index.js.map +1 -0
  65. package/dist/security/index.mjs +211 -0
  66. package/dist/security/index.mjs.map +1 -0
  67. package/dist/shopify-api/index.d.cts +139 -0
  68. package/dist/shopify-api/index.d.ts +139 -0
  69. package/dist/shopify-api/index.js +469 -0
  70. package/dist/shopify-api/index.js.map +1 -0
  71. package/dist/shopify-api/index.mjs +439 -0
  72. package/dist/shopify-api/index.mjs.map +1 -0
  73. package/dist/shopify-compliance/index.d.cts +85 -0
  74. package/dist/shopify-compliance/index.d.ts +85 -0
  75. package/dist/shopify-compliance/index.js +287 -0
  76. package/dist/shopify-compliance/index.js.map +1 -0
  77. package/dist/shopify-compliance/index.mjs +259 -0
  78. package/dist/shopify-compliance/index.mjs.map +1 -0
  79. package/dist/webhooks/index.d.cts +127 -0
  80. package/dist/webhooks/index.d.ts +127 -0
  81. package/dist/webhooks/index.js +18934 -0
  82. package/dist/webhooks/index.js.map +1 -0
  83. package/dist/webhooks/index.mjs +18916 -0
  84. package/dist/webhooks/index.mjs.map +1 -0
  85. package/package.json +112 -0
@@ -0,0 +1,469 @@
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/shopify-api/index.ts
21
+ var shopify_api_exports = {};
22
+ __export(shopify_api_exports, {
23
+ GraphQLHandler: () => GraphQLHandler,
24
+ MockShopifyServer: () => MockShopifyServer,
25
+ OAuthHandler: () => OAuthHandler,
26
+ RESTHandler: () => RESTHandler
27
+ });
28
+ module.exports = __toCommonJS(shopify_api_exports);
29
+
30
+ // src/shopify-api/mock-server.ts
31
+ var import_node_http = require("http");
32
+
33
+ // src/shopify-api/graphql-handler.ts
34
+ var GraphQLHandler = class {
35
+ operations = /* @__PURE__ */ new Map();
36
+ sequences = /* @__PURE__ */ new Map();
37
+ queryPatterns = [];
38
+ defaultResponse = null;
39
+ rateLimitConfig = null;
40
+ onOperation(operationName, response) {
41
+ this.operations.set(operationName, { response });
42
+ }
43
+ onQuery(queryPattern, response) {
44
+ this.queryPatterns.push({ pattern: queryPattern, response });
45
+ }
46
+ onOperationSequence(operationName, responses) {
47
+ this.sequences.set(operationName, { responses, index: 0 });
48
+ }
49
+ enableRateLimiting(config) {
50
+ const maxCost = config?.maxCost ?? 1e3;
51
+ const restoreRate = config?.restoreRate ?? 50;
52
+ this.rateLimitConfig = {
53
+ maxCost,
54
+ restoreRate,
55
+ currentBudget: maxCost,
56
+ lastRestoreTime: Date.now()
57
+ };
58
+ }
59
+ disableRateLimiting() {
60
+ this.rateLimitConfig = null;
61
+ }
62
+ setDefaultResponse(response) {
63
+ this.defaultResponse = response;
64
+ }
65
+ handle(body) {
66
+ if (this.rateLimitConfig) {
67
+ this.restoreBudget();
68
+ }
69
+ const operationName = body.operationName ?? this.extractOperationName(body.query);
70
+ if (operationName) {
71
+ const sequence = this.sequences.get(operationName);
72
+ if (sequence) {
73
+ const response = sequence.responses[sequence.index] ?? sequence.responses[sequence.responses.length - 1];
74
+ if (sequence.index < sequence.responses.length) {
75
+ sequence.index++;
76
+ }
77
+ return this.applyRateLimiting(response);
78
+ }
79
+ }
80
+ if (operationName) {
81
+ const registration = this.operations.get(operationName);
82
+ if (registration) {
83
+ return this.applyRateLimiting(registration.response);
84
+ }
85
+ }
86
+ for (const { pattern, response } of this.queryPatterns) {
87
+ if (typeof pattern === "string") {
88
+ if (body.query.includes(pattern)) {
89
+ return this.applyRateLimiting(response);
90
+ }
91
+ } else {
92
+ if (pattern.test(body.query)) {
93
+ return this.applyRateLimiting(response);
94
+ }
95
+ }
96
+ }
97
+ if (this.defaultResponse) {
98
+ return this.applyRateLimiting(this.defaultResponse);
99
+ }
100
+ return {
101
+ errors: [{ message: `No mock registered for operation: ${operationName ?? "unknown"}` }]
102
+ };
103
+ }
104
+ reset() {
105
+ this.operations.clear();
106
+ this.sequences.clear();
107
+ this.queryPatterns = [];
108
+ this.defaultResponse = null;
109
+ this.rateLimitConfig = null;
110
+ }
111
+ extractOperationName(query) {
112
+ const match = query.match(/(?:query|mutation|subscription)\s+(\w+)/);
113
+ return match?.[1];
114
+ }
115
+ restoreBudget() {
116
+ if (!this.rateLimitConfig) return;
117
+ const now = Date.now();
118
+ const elapsed = (now - this.rateLimitConfig.lastRestoreTime) / 1e3;
119
+ const restored = elapsed * this.rateLimitConfig.restoreRate;
120
+ this.rateLimitConfig.currentBudget = Math.min(
121
+ this.rateLimitConfig.maxCost,
122
+ this.rateLimitConfig.currentBudget + restored
123
+ );
124
+ this.rateLimitConfig.lastRestoreTime = now;
125
+ }
126
+ applyRateLimiting(response) {
127
+ if (!this.rateLimitConfig) return response;
128
+ const cost = response.extensions?.cost?.actualQueryCost ?? 10;
129
+ if (this.rateLimitConfig.currentBudget < cost) {
130
+ return {
131
+ errors: [{ message: "Throttled" }],
132
+ extensions: {
133
+ cost: {
134
+ requestedQueryCost: cost,
135
+ actualQueryCost: cost,
136
+ throttleStatus: {
137
+ maximumAvailable: this.rateLimitConfig.maxCost,
138
+ currentlyAvailable: this.rateLimitConfig.currentBudget,
139
+ restoreRate: this.rateLimitConfig.restoreRate
140
+ }
141
+ }
142
+ }
143
+ };
144
+ }
145
+ this.rateLimitConfig.currentBudget -= cost;
146
+ const extensions = response.extensions ?? {
147
+ cost: {
148
+ requestedQueryCost: cost,
149
+ actualQueryCost: cost,
150
+ throttleStatus: {
151
+ maximumAvailable: this.rateLimitConfig.maxCost,
152
+ currentlyAvailable: this.rateLimitConfig.currentBudget,
153
+ restoreRate: this.rateLimitConfig.restoreRate
154
+ }
155
+ }
156
+ };
157
+ return { ...response, extensions };
158
+ }
159
+ };
160
+
161
+ // src/shopify-api/rest-handler.ts
162
+ var RESTHandler = class {
163
+ routes = [];
164
+ onRequest(method, pathPattern, response) {
165
+ this.routes.push({ method: method.toUpperCase(), pattern: pathPattern, response });
166
+ }
167
+ onGet(pathPattern, response) {
168
+ this.onRequest("GET", pathPattern, response);
169
+ }
170
+ onPost(pathPattern, response) {
171
+ this.onRequest("POST", pathPattern, response);
172
+ }
173
+ onPut(pathPattern, response) {
174
+ this.onRequest("PUT", pathPattern, response);
175
+ }
176
+ onDelete(pathPattern, response) {
177
+ this.onRequest("DELETE", pathPattern, response);
178
+ }
179
+ handle(method, path, _body) {
180
+ const upperMethod = method.toUpperCase();
181
+ for (const route of this.routes) {
182
+ if (route.method !== upperMethod) continue;
183
+ if (typeof route.pattern === "string") {
184
+ if (path === route.pattern || path.startsWith(route.pattern)) {
185
+ return route.response;
186
+ }
187
+ } else {
188
+ if (route.pattern.test(path)) {
189
+ return route.response;
190
+ }
191
+ }
192
+ }
193
+ return { status: 404, body: { errors: "Not Found" } };
194
+ }
195
+ reset() {
196
+ this.routes = [];
197
+ }
198
+ };
199
+
200
+ // src/shopify-api/oauth-handler.ts
201
+ var import_node_crypto = require("crypto");
202
+ var OAuthHandler = class {
203
+ config;
204
+ generatedCodes = /* @__PURE__ */ new Map();
205
+ constructor(config) {
206
+ this.config = {
207
+ accessToken: "shpat_test_token_000000000000000000000000",
208
+ scope: "read_products,write_products",
209
+ expiresIn: 86400,
210
+ ...config
211
+ };
212
+ }
213
+ configure(config) {
214
+ this.config = { ...this.config, ...config };
215
+ }
216
+ handleAuthorize(query) {
217
+ const code = (0, import_node_crypto.randomBytes)(16).toString("hex");
218
+ const redirectUri = query["redirect_uri"] ?? "";
219
+ const state = query["state"] ?? "";
220
+ const shop = query["shop"] ?? "test-shop.myshopify.com";
221
+ this.generatedCodes.set(code, { nonce: state, shop });
222
+ const url = new URL(redirectUri || "https://localhost/callback");
223
+ url.searchParams.set("code", code);
224
+ url.searchParams.set("shop", shop);
225
+ url.searchParams.set("state", state);
226
+ url.searchParams.set("hmac", "mock_hmac_value");
227
+ url.searchParams.set("timestamp", String(Math.floor(Date.now() / 1e3)));
228
+ return { redirectUrl: url.toString(), code };
229
+ }
230
+ handleAccessToken(_body) {
231
+ const response = {
232
+ access_token: this.config.accessToken,
233
+ scope: this.config.scope
234
+ };
235
+ if (this.config.expiresIn !== void 0) {
236
+ response["expires_in"] = this.config.expiresIn;
237
+ }
238
+ if (this.config.associatedUser !== void 0) {
239
+ response["associated_user"] = {
240
+ id: this.config.associatedUser.id,
241
+ first_name: this.config.associatedUser.firstName,
242
+ last_name: this.config.associatedUser.lastName,
243
+ email: this.config.associatedUser.email,
244
+ email_verified: this.config.associatedUser.emailVerified,
245
+ account_owner: this.config.associatedUser.accountOwner,
246
+ locale: this.config.associatedUser.locale,
247
+ collaborator: this.config.associatedUser.collaborator
248
+ };
249
+ response["associated_user_scope"] = this.config.scope;
250
+ }
251
+ return { status: 200, body: response };
252
+ }
253
+ handleTokenExchange(_body) {
254
+ const response = {
255
+ access_token: this.config.accessToken,
256
+ scope: this.config.scope,
257
+ token_type: "bearer",
258
+ expires_in: this.config.expiresIn ?? 86400
259
+ };
260
+ if (this.config.associatedUser !== void 0) {
261
+ response["associated_user"] = {
262
+ id: this.config.associatedUser.id,
263
+ first_name: this.config.associatedUser.firstName,
264
+ last_name: this.config.associatedUser.lastName,
265
+ email: this.config.associatedUser.email,
266
+ email_verified: this.config.associatedUser.emailVerified,
267
+ account_owner: this.config.associatedUser.accountOwner,
268
+ locale: this.config.associatedUser.locale,
269
+ collaborator: this.config.associatedUser.collaborator
270
+ };
271
+ response["associated_user_scope"] = this.config.scope;
272
+ }
273
+ return { status: 200, body: response };
274
+ }
275
+ reset() {
276
+ this.generatedCodes.clear();
277
+ this.config = {
278
+ accessToken: "shpat_test_token_000000000000000000000000",
279
+ scope: "read_products,write_products",
280
+ expiresIn: 86400
281
+ };
282
+ }
283
+ };
284
+
285
+ // src/shopify-api/mock-server.ts
286
+ function readBody(req) {
287
+ return new Promise((resolve, reject) => {
288
+ const chunks = [];
289
+ req.on("data", (chunk) => chunks.push(chunk));
290
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
291
+ req.on("error", reject);
292
+ });
293
+ }
294
+ function parseQuery(url) {
295
+ const query = {};
296
+ url.searchParams.forEach((value, key) => {
297
+ query[key] = value;
298
+ });
299
+ return query;
300
+ }
301
+ function delay(ms) {
302
+ return new Promise((resolve) => setTimeout(resolve, ms));
303
+ }
304
+ var MockShopifyServer = class {
305
+ graphql;
306
+ rest;
307
+ oauth;
308
+ server = null;
309
+ serverPort = 0;
310
+ config;
311
+ requests = [];
312
+ constructor(config) {
313
+ this.config = {
314
+ port: config?.port ?? 0,
315
+ apiVersion: config?.apiVersion ?? "2024-01",
316
+ defaultShop: config?.defaultShop ?? "test-shop.myshopify.com",
317
+ latencyMs: config?.latencyMs ?? 0
318
+ };
319
+ this.graphql = new GraphQLHandler();
320
+ this.rest = new RESTHandler();
321
+ this.oauth = new OAuthHandler();
322
+ }
323
+ async start() {
324
+ return new Promise((resolve, reject) => {
325
+ this.server = (0, import_node_http.createServer)((req, res) => {
326
+ this.handleRequest(req, res).catch((err) => {
327
+ res.writeHead(500, { "Content-Type": "application/json" });
328
+ res.end(JSON.stringify({ error: String(err) }));
329
+ });
330
+ });
331
+ this.server.on("error", reject);
332
+ this.server.listen(this.config.port, "127.0.0.1", () => {
333
+ const addr = this.server.address();
334
+ if (addr && typeof addr === "object") {
335
+ this.serverPort = addr.port;
336
+ }
337
+ resolve({ port: this.serverPort, url: this.url });
338
+ });
339
+ });
340
+ }
341
+ async stop() {
342
+ return new Promise((resolve, reject) => {
343
+ if (!this.server) {
344
+ resolve();
345
+ return;
346
+ }
347
+ this.server.close((err) => {
348
+ this.server = null;
349
+ if (err) reject(err);
350
+ else resolve();
351
+ });
352
+ });
353
+ }
354
+ get url() {
355
+ return `http://127.0.0.1:${this.serverPort}`;
356
+ }
357
+ get port() {
358
+ return this.serverPort;
359
+ }
360
+ getRequests() {
361
+ return [...this.requests];
362
+ }
363
+ getLastRequest() {
364
+ return this.requests[this.requests.length - 1];
365
+ }
366
+ getRequestsByPath(pathPattern) {
367
+ return this.requests.filter((req) => {
368
+ if (typeof pathPattern === "string") {
369
+ return req.path.includes(pathPattern);
370
+ }
371
+ return pathPattern.test(req.path);
372
+ });
373
+ }
374
+ clearRequests() {
375
+ this.requests = [];
376
+ }
377
+ reset() {
378
+ this.graphql.reset();
379
+ this.rest.reset();
380
+ this.oauth.reset();
381
+ this.requests = [];
382
+ }
383
+ async handleRequest(req, res) {
384
+ if (this.config.latencyMs > 0) {
385
+ await delay(this.config.latencyMs);
386
+ }
387
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "127.0.0.1"}`);
388
+ const method = (req.method ?? "GET").toUpperCase();
389
+ const path = url.pathname;
390
+ const query = parseQuery(url);
391
+ const rawBody = await readBody(req);
392
+ let body = null;
393
+ if (rawBody) {
394
+ try {
395
+ body = JSON.parse(rawBody);
396
+ } catch {
397
+ body = rawBody;
398
+ }
399
+ }
400
+ const headers = {};
401
+ for (const [key, value] of Object.entries(req.headers)) {
402
+ if (typeof value === "string") {
403
+ headers[key] = value;
404
+ } else if (Array.isArray(value)) {
405
+ headers[key] = value.join(", ");
406
+ }
407
+ }
408
+ this.requests.push({
409
+ method,
410
+ path,
411
+ headers,
412
+ body,
413
+ query,
414
+ timestamp: /* @__PURE__ */ new Date()
415
+ });
416
+ if (path === "/admin/oauth/authorize") {
417
+ const result = this.oauth.handleAuthorize(query);
418
+ res.writeHead(302, { Location: result.redirectUrl });
419
+ res.end();
420
+ return;
421
+ }
422
+ if (path === `/admin/oauth/access_token` && method === "POST" && typeof body === "object" && body !== null && "grant_type" in body) {
423
+ const tokenBody = body;
424
+ if (tokenBody["grant_type"] === "urn:ietf:params:oauth:grant-type:token-exchange") {
425
+ const result = this.oauth.handleTokenExchange(tokenBody);
426
+ res.writeHead(result.status, { "Content-Type": "application/json" });
427
+ res.end(JSON.stringify(result.body));
428
+ return;
429
+ }
430
+ }
431
+ if (path === "/admin/oauth/access_token" && method === "POST") {
432
+ const tokenBody = typeof body === "object" && body !== null ? body : {};
433
+ const result = this.oauth.handleAccessToken(tokenBody);
434
+ res.writeHead(result.status, { "Content-Type": "application/json" });
435
+ res.end(JSON.stringify(result.body));
436
+ return;
437
+ }
438
+ const graphqlPath = `/admin/api/${this.config.apiVersion}/graphql.json`;
439
+ if (path === graphqlPath && method === "POST") {
440
+ const graphqlBody = body;
441
+ const result = this.graphql.handle(graphqlBody);
442
+ const statusCode = result.errors?.some((e) => e.message === "Throttled") ? 429 : 200;
443
+ res.writeHead(statusCode, { "Content-Type": "application/json" });
444
+ res.end(JSON.stringify(result));
445
+ return;
446
+ }
447
+ const restPathPrefix = `/admin/api/${this.config.apiVersion}/`;
448
+ if (path.startsWith(restPathPrefix)) {
449
+ const result = this.rest.handle(method, path, body);
450
+ const responseHeaders = {
451
+ "Content-Type": "application/json",
452
+ ...result.headers
453
+ };
454
+ res.writeHead(result.status ?? 200, responseHeaders);
455
+ res.end(JSON.stringify(result.body ?? {}));
456
+ return;
457
+ }
458
+ res.writeHead(404, { "Content-Type": "application/json" });
459
+ res.end(JSON.stringify({ errors: "Not Found" }));
460
+ }
461
+ };
462
+ // Annotate the CommonJS export names for ESM import in node:
463
+ 0 && (module.exports = {
464
+ GraphQLHandler,
465
+ MockShopifyServer,
466
+ OAuthHandler,
467
+ RESTHandler
468
+ });
469
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shopify-api/index.ts","../../src/shopify-api/mock-server.ts","../../src/shopify-api/graphql-handler.ts","../../src/shopify-api/rest-handler.ts","../../src/shopify-api/oauth-handler.ts"],"sourcesContent":["/**\n * @uniforge/testing - Shopify API\n *\n * Mock Shopify API server for integration and contract testing.\n */\n\nexport { MockShopifyServer } from './mock-server';\nexport { GraphQLHandler } from './graphql-handler';\nexport { RESTHandler } from './rest-handler';\nexport { OAuthHandler } from './oauth-handler';\nexport type {\n MockShopifyServerConfig,\n MockServerRequest,\n GraphQLResponseConfig,\n RESTResponseConfig,\n OAuthConfig,\n} from './types';\n","import { createServer, type IncomingMessage, type ServerResponse, type Server } from 'node:http';\nimport { GraphQLHandler } from './graphql-handler';\nimport { RESTHandler } from './rest-handler';\nimport { OAuthHandler } from './oauth-handler';\nimport type { MockShopifyServerConfig, MockServerRequest } from './types';\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));\n req.on('error', reject);\n });\n}\n\nfunction parseQuery(url: URL): Record<string, string> {\n const query: Record<string, string> = {};\n url.searchParams.forEach((value, key) => {\n query[key] = value;\n });\n return query;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport class MockShopifyServer {\n readonly graphql: GraphQLHandler;\n readonly rest: RESTHandler;\n readonly oauth: OAuthHandler;\n\n private server: Server | null = null;\n private serverPort = 0;\n private readonly config: Required<MockShopifyServerConfig>;\n private requests: MockServerRequest[] = [];\n\n constructor(config?: MockShopifyServerConfig) {\n this.config = {\n port: config?.port ?? 0,\n apiVersion: config?.apiVersion ?? '2024-01',\n defaultShop: config?.defaultShop ?? 'test-shop.myshopify.com',\n latencyMs: config?.latencyMs ?? 0,\n };\n this.graphql = new GraphQLHandler();\n this.rest = new RESTHandler();\n this.oauth = new OAuthHandler();\n }\n\n async start(): Promise<{ port: number; url: string }> {\n return new Promise((resolve, reject) => {\n this.server = createServer((req, res) => {\n this.handleRequest(req, res).catch((err) => {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: String(err) }));\n });\n });\n\n this.server.on('error', reject);\n\n this.server.listen(this.config.port, '127.0.0.1', () => {\n const addr = this.server!.address();\n if (addr && typeof addr === 'object') {\n this.serverPort = addr.port;\n }\n resolve({ port: this.serverPort, url: this.url });\n });\n });\n }\n\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.server) {\n resolve();\n return;\n }\n this.server.close((err) => {\n this.server = null;\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n get url(): string {\n return `http://127.0.0.1:${this.serverPort}`;\n }\n\n get port(): number {\n return this.serverPort;\n }\n\n getRequests(): MockServerRequest[] {\n return [...this.requests];\n }\n\n getLastRequest(): MockServerRequest | undefined {\n return this.requests[this.requests.length - 1];\n }\n\n getRequestsByPath(pathPattern: string | RegExp): MockServerRequest[] {\n return this.requests.filter((req) => {\n if (typeof pathPattern === 'string') {\n return req.path.includes(pathPattern);\n }\n return pathPattern.test(req.path);\n });\n }\n\n clearRequests(): void {\n this.requests = [];\n }\n\n reset(): void {\n this.graphql.reset();\n this.rest.reset();\n this.oauth.reset();\n this.requests = [];\n }\n\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n if (this.config.latencyMs > 0) {\n await delay(this.config.latencyMs);\n }\n\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? '127.0.0.1'}`);\n const method = (req.method ?? 'GET').toUpperCase();\n const path = url.pathname;\n const query = parseQuery(url);\n const rawBody = await readBody(req);\n let body: unknown = null;\n\n if (rawBody) {\n try {\n body = JSON.parse(rawBody);\n } catch {\n body = rawBody;\n }\n }\n\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === 'string') {\n headers[key] = value;\n } else if (Array.isArray(value)) {\n headers[key] = value.join(', ');\n }\n }\n\n this.requests.push({\n method,\n path,\n headers,\n body,\n query,\n timestamp: new Date(),\n });\n\n // OAuth authorize\n if (path === '/admin/oauth/authorize') {\n const result = this.oauth.handleAuthorize(query);\n res.writeHead(302, { Location: result.redirectUrl });\n res.end();\n return;\n }\n\n // Token exchange (must be checked before generic OAuth access token)\n if (path === `/admin/oauth/access_token` && method === 'POST' && typeof body === 'object' && body !== null && 'grant_type' in body) {\n const tokenBody = body as Record<string, string>;\n if (tokenBody['grant_type'] === 'urn:ietf:params:oauth:grant-type:token-exchange') {\n const result = this.oauth.handleTokenExchange(tokenBody);\n res.writeHead(result.status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(result.body));\n return;\n }\n }\n\n // OAuth access token\n if (path === '/admin/oauth/access_token' && method === 'POST') {\n const tokenBody = typeof body === 'object' && body !== null ? body as Record<string, string> : {};\n const result = this.oauth.handleAccessToken(tokenBody);\n res.writeHead(result.status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(result.body));\n return;\n }\n\n // GraphQL endpoint\n const graphqlPath = `/admin/api/${this.config.apiVersion}/graphql.json`;\n if (path === graphqlPath && method === 'POST') {\n const graphqlBody = body as { query: string; variables?: Record<string, unknown>; operationName?: string };\n const result = this.graphql.handle(graphqlBody);\n\n const statusCode = result.errors?.some((e) => e.message === 'Throttled') ? 429 : 200;\n res.writeHead(statusCode, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(result));\n return;\n }\n\n // REST endpoints\n const restPathPrefix = `/admin/api/${this.config.apiVersion}/`;\n if (path.startsWith(restPathPrefix)) {\n const result = this.rest.handle(method, path, body);\n const responseHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...result.headers,\n };\n res.writeHead(result.status ?? 200, responseHeaders);\n res.end(JSON.stringify(result.body ?? {}));\n return;\n }\n\n // Unmatched\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ errors: 'Not Found' }));\n }\n}\n","import type { GraphQLResponseConfig } from './types';\n\ninterface OperationRegistration {\n response: GraphQLResponseConfig;\n}\n\ninterface SequenceRegistration {\n responses: GraphQLResponseConfig[];\n index: number;\n}\n\ninterface QueryPatternRegistration {\n pattern: string | RegExp;\n response: GraphQLResponseConfig;\n}\n\ninterface RateLimitConfig {\n maxCost: number;\n restoreRate: number;\n currentBudget: number;\n lastRestoreTime: number;\n}\n\nexport class GraphQLHandler {\n private operations = new Map<string, OperationRegistration>();\n private sequences = new Map<string, SequenceRegistration>();\n private queryPatterns: QueryPatternRegistration[] = [];\n private defaultResponse: GraphQLResponseConfig | null = null;\n private rateLimitConfig: RateLimitConfig | null = null;\n\n onOperation(operationName: string, response: GraphQLResponseConfig): void {\n this.operations.set(operationName, { response });\n }\n\n onQuery(queryPattern: string | RegExp, response: GraphQLResponseConfig): void {\n this.queryPatterns.push({ pattern: queryPattern, response });\n }\n\n onOperationSequence(operationName: string, responses: GraphQLResponseConfig[]): void {\n this.sequences.set(operationName, { responses, index: 0 });\n }\n\n enableRateLimiting(config?: { maxCost?: number; restoreRate?: number }): void {\n const maxCost = config?.maxCost ?? 1000;\n const restoreRate = config?.restoreRate ?? 50;\n this.rateLimitConfig = {\n maxCost,\n restoreRate,\n currentBudget: maxCost,\n lastRestoreTime: Date.now(),\n };\n }\n\n disableRateLimiting(): void {\n this.rateLimitConfig = null;\n }\n\n setDefaultResponse(response: GraphQLResponseConfig): void {\n this.defaultResponse = response;\n }\n\n handle(body: {\n query: string;\n variables?: Record<string, unknown>;\n operationName?: string;\n }): GraphQLResponseConfig {\n if (this.rateLimitConfig) {\n this.restoreBudget();\n }\n\n const operationName = body.operationName ?? this.extractOperationName(body.query);\n\n // Check sequences first\n if (operationName) {\n const sequence = this.sequences.get(operationName);\n if (sequence) {\n const response = sequence.responses[sequence.index] ?? sequence.responses[sequence.responses.length - 1]!;\n if (sequence.index < sequence.responses.length) {\n sequence.index++;\n }\n return this.applyRateLimiting(response!);\n }\n }\n\n // Check exact operation name match\n if (operationName) {\n const registration = this.operations.get(operationName);\n if (registration) {\n return this.applyRateLimiting(registration.response);\n }\n }\n\n // Check query patterns\n for (const { pattern, response } of this.queryPatterns) {\n if (typeof pattern === 'string') {\n if (body.query.includes(pattern)) {\n return this.applyRateLimiting(response);\n }\n } else {\n if (pattern.test(body.query)) {\n return this.applyRateLimiting(response);\n }\n }\n }\n\n // Fall back to default\n if (this.defaultResponse) {\n return this.applyRateLimiting(this.defaultResponse);\n }\n\n return {\n errors: [{ message: `No mock registered for operation: ${operationName ?? 'unknown'}` }],\n };\n }\n\n reset(): void {\n this.operations.clear();\n this.sequences.clear();\n this.queryPatterns = [];\n this.defaultResponse = null;\n this.rateLimitConfig = null;\n }\n\n private extractOperationName(query: string): string | undefined {\n const match = query.match(/(?:query|mutation|subscription)\\s+(\\w+)/);\n return match?.[1];\n }\n\n private restoreBudget(): void {\n if (!this.rateLimitConfig) return;\n const now = Date.now();\n const elapsed = (now - this.rateLimitConfig.lastRestoreTime) / 1000;\n const restored = elapsed * this.rateLimitConfig.restoreRate;\n this.rateLimitConfig.currentBudget = Math.min(\n this.rateLimitConfig.maxCost,\n this.rateLimitConfig.currentBudget + restored,\n );\n this.rateLimitConfig.lastRestoreTime = now;\n }\n\n private applyRateLimiting(response: GraphQLResponseConfig): GraphQLResponseConfig {\n if (!this.rateLimitConfig) return response;\n\n const cost = response.extensions?.cost?.actualQueryCost ?? 10;\n\n if (this.rateLimitConfig.currentBudget < cost) {\n return {\n errors: [{ message: 'Throttled' }],\n extensions: {\n cost: {\n requestedQueryCost: cost,\n actualQueryCost: cost,\n throttleStatus: {\n maximumAvailable: this.rateLimitConfig.maxCost,\n currentlyAvailable: this.rateLimitConfig.currentBudget,\n restoreRate: this.rateLimitConfig.restoreRate,\n },\n },\n },\n };\n }\n\n this.rateLimitConfig.currentBudget -= cost;\n\n const extensions = response.extensions ?? {\n cost: {\n requestedQueryCost: cost,\n actualQueryCost: cost,\n throttleStatus: {\n maximumAvailable: this.rateLimitConfig.maxCost,\n currentlyAvailable: this.rateLimitConfig.currentBudget,\n restoreRate: this.rateLimitConfig.restoreRate,\n },\n },\n };\n\n return { ...response, extensions };\n }\n}\n","import type { RESTResponseConfig } from './types';\n\ninterface RouteRegistration {\n method: string;\n pattern: string | RegExp;\n response: RESTResponseConfig;\n}\n\nexport class RESTHandler {\n private routes: RouteRegistration[] = [];\n\n onRequest(method: string, pathPattern: string | RegExp, response: RESTResponseConfig): void {\n this.routes.push({ method: method.toUpperCase(), pattern: pathPattern, response });\n }\n\n onGet(pathPattern: string | RegExp, response: RESTResponseConfig): void {\n this.onRequest('GET', pathPattern, response);\n }\n\n onPost(pathPattern: string | RegExp, response: RESTResponseConfig): void {\n this.onRequest('POST', pathPattern, response);\n }\n\n onPut(pathPattern: string | RegExp, response: RESTResponseConfig): void {\n this.onRequest('PUT', pathPattern, response);\n }\n\n onDelete(pathPattern: string | RegExp, response: RESTResponseConfig): void {\n this.onRequest('DELETE', pathPattern, response);\n }\n\n handle(method: string, path: string, _body?: unknown): RESTResponseConfig {\n const upperMethod = method.toUpperCase();\n\n for (const route of this.routes) {\n if (route.method !== upperMethod) continue;\n\n if (typeof route.pattern === 'string') {\n if (path === route.pattern || path.startsWith(route.pattern)) {\n return route.response;\n }\n } else {\n if (route.pattern.test(path)) {\n return route.response;\n }\n }\n }\n\n return { status: 404, body: { errors: 'Not Found' } };\n }\n\n reset(): void {\n this.routes = [];\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport type { OAuthConfig } from './types';\n\nexport class OAuthHandler {\n private config: OAuthConfig;\n private generatedCodes = new Map<string, { nonce: string; shop: string }>();\n\n constructor(config?: OAuthConfig) {\n this.config = {\n accessToken: 'shpat_test_token_000000000000000000000000',\n scope: 'read_products,write_products',\n expiresIn: 86400,\n ...config,\n };\n }\n\n configure(config: OAuthConfig): void {\n this.config = { ...this.config, ...config };\n }\n\n handleAuthorize(query: Record<string, string>): { redirectUrl: string; code: string } {\n const code = randomBytes(16).toString('hex');\n const redirectUri = query['redirect_uri'] ?? '';\n const state = query['state'] ?? '';\n const shop = query['shop'] ?? 'test-shop.myshopify.com';\n\n this.generatedCodes.set(code, { nonce: state, shop });\n\n const url = new URL(redirectUri || 'https://localhost/callback');\n url.searchParams.set('code', code);\n url.searchParams.set('shop', shop);\n url.searchParams.set('state', state);\n url.searchParams.set('hmac', 'mock_hmac_value');\n url.searchParams.set('timestamp', String(Math.floor(Date.now() / 1000)));\n\n return { redirectUrl: url.toString(), code };\n }\n\n handleAccessToken(_body: Record<string, string>): { status: number; body: unknown } {\n const response: Record<string, unknown> = {\n access_token: this.config.accessToken,\n scope: this.config.scope,\n };\n\n if (this.config.expiresIn !== undefined) {\n response['expires_in'] = this.config.expiresIn;\n }\n\n if (this.config.associatedUser !== undefined) {\n response['associated_user'] = {\n id: this.config.associatedUser.id,\n first_name: this.config.associatedUser.firstName,\n last_name: this.config.associatedUser.lastName,\n email: this.config.associatedUser.email,\n email_verified: this.config.associatedUser.emailVerified,\n account_owner: this.config.associatedUser.accountOwner,\n locale: this.config.associatedUser.locale,\n collaborator: this.config.associatedUser.collaborator,\n };\n response['associated_user_scope'] = this.config.scope;\n }\n\n return { status: 200, body: response };\n }\n\n handleTokenExchange(_body: Record<string, string>): { status: number; body: unknown } {\n const response: Record<string, unknown> = {\n access_token: this.config.accessToken,\n scope: this.config.scope,\n token_type: 'bearer',\n expires_in: this.config.expiresIn ?? 86400,\n };\n\n if (this.config.associatedUser !== undefined) {\n response['associated_user'] = {\n id: this.config.associatedUser.id,\n first_name: this.config.associatedUser.firstName,\n last_name: this.config.associatedUser.lastName,\n email: this.config.associatedUser.email,\n email_verified: this.config.associatedUser.emailVerified,\n account_owner: this.config.associatedUser.accountOwner,\n locale: this.config.associatedUser.locale,\n collaborator: this.config.associatedUser.collaborator,\n };\n response['associated_user_scope'] = this.config.scope;\n }\n\n return { status: 200, body: response };\n }\n\n reset(): void {\n this.generatedCodes.clear();\n this.config = {\n accessToken: 'shpat_test_token_000000000000000000000000',\n scope: 'read_products,write_products',\n expiresIn: 86400,\n };\n }\n}\n"],"mappings":"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","names":[]}