@classytic/arc 1.0.0

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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +900 -0
  3. package/bin/arc.js +344 -0
  4. package/dist/adapters/index.d.ts +237 -0
  5. package/dist/adapters/index.js +668 -0
  6. package/dist/arcCorePlugin-DTPWXcZN.d.ts +273 -0
  7. package/dist/audit/index.d.ts +195 -0
  8. package/dist/audit/index.js +319 -0
  9. package/dist/auth/index.d.ts +47 -0
  10. package/dist/auth/index.js +174 -0
  11. package/dist/cli/commands/docs.d.ts +11 -0
  12. package/dist/cli/commands/docs.js +474 -0
  13. package/dist/cli/commands/introspect.d.ts +8 -0
  14. package/dist/cli/commands/introspect.js +338 -0
  15. package/dist/cli/index.d.ts +43 -0
  16. package/dist/cli/index.js +520 -0
  17. package/dist/createApp-pzUAkzbz.d.ts +77 -0
  18. package/dist/docs/index.d.ts +166 -0
  19. package/dist/docs/index.js +650 -0
  20. package/dist/errors-8WIxGS_6.d.ts +122 -0
  21. package/dist/events/index.d.ts +117 -0
  22. package/dist/events/index.js +89 -0
  23. package/dist/factory/index.d.ts +38 -0
  24. package/dist/factory/index.js +1664 -0
  25. package/dist/hooks/index.d.ts +4 -0
  26. package/dist/hooks/index.js +199 -0
  27. package/dist/idempotency/index.d.ts +323 -0
  28. package/dist/idempotency/index.js +500 -0
  29. package/dist/index-DkAW8BXh.d.ts +1302 -0
  30. package/dist/index.d.ts +331 -0
  31. package/dist/index.js +4734 -0
  32. package/dist/migrations/index.d.ts +185 -0
  33. package/dist/migrations/index.js +274 -0
  34. package/dist/org/index.d.ts +129 -0
  35. package/dist/org/index.js +220 -0
  36. package/dist/permissions/index.d.ts +144 -0
  37. package/dist/permissions/index.js +100 -0
  38. package/dist/plugins/index.d.ts +46 -0
  39. package/dist/plugins/index.js +1069 -0
  40. package/dist/policies/index.d.ts +398 -0
  41. package/dist/policies/index.js +196 -0
  42. package/dist/presets/index.d.ts +336 -0
  43. package/dist/presets/index.js +382 -0
  44. package/dist/presets/multiTenant.d.ts +39 -0
  45. package/dist/presets/multiTenant.js +112 -0
  46. package/dist/registry/index.d.ts +16 -0
  47. package/dist/registry/index.js +253 -0
  48. package/dist/testing/index.d.ts +618 -0
  49. package/dist/testing/index.js +48032 -0
  50. package/dist/types/index.d.ts +4 -0
  51. package/dist/types/index.js +8 -0
  52. package/dist/types-0IPhH_NR.d.ts +143 -0
  53. package/dist/types-B99TBmFV.d.ts +76 -0
  54. package/dist/utils/index.d.ts +655 -0
  55. package/dist/utils/index.js +905 -0
  56. package/package.json +227 -0
@@ -0,0 +1,905 @@
1
+ // src/utils/errors.ts
2
+ var ArcError = class extends Error {
3
+ name;
4
+ code;
5
+ statusCode;
6
+ details;
7
+ cause;
8
+ timestamp;
9
+ requestId;
10
+ constructor(message, options = {}) {
11
+ super(message);
12
+ this.name = "ArcError";
13
+ this.code = options.code ?? "ARC_ERROR";
14
+ this.statusCode = options.statusCode ?? 500;
15
+ this.details = options.details;
16
+ this.cause = options.cause;
17
+ this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
18
+ this.requestId = options.requestId;
19
+ if (Error.captureStackTrace) {
20
+ Error.captureStackTrace(this, this.constructor);
21
+ }
22
+ }
23
+ /**
24
+ * Set request ID (typically from request context)
25
+ */
26
+ withRequestId(requestId) {
27
+ this.requestId = requestId;
28
+ return this;
29
+ }
30
+ /**
31
+ * Convert to JSON response
32
+ */
33
+ toJSON() {
34
+ return {
35
+ success: false,
36
+ error: this.message,
37
+ code: this.code,
38
+ timestamp: this.timestamp,
39
+ ...this.requestId && { requestId: this.requestId },
40
+ ...this.details && { details: this.details }
41
+ };
42
+ }
43
+ };
44
+ var NotFoundError = class extends ArcError {
45
+ constructor(resource, identifier) {
46
+ const message = identifier ? `${resource} with identifier '${identifier}' not found` : `${resource} not found`;
47
+ super(message, {
48
+ code: "NOT_FOUND",
49
+ statusCode: 404,
50
+ details: { resource, identifier }
51
+ });
52
+ this.name = "NotFoundError";
53
+ }
54
+ };
55
+ var ValidationError = class extends ArcError {
56
+ errors;
57
+ constructor(message, errors = []) {
58
+ super(message, {
59
+ code: "VALIDATION_ERROR",
60
+ statusCode: 400,
61
+ details: { errors }
62
+ });
63
+ this.name = "ValidationError";
64
+ this.errors = errors;
65
+ }
66
+ };
67
+ var UnauthorizedError = class extends ArcError {
68
+ constructor(message = "Authentication required") {
69
+ super(message, {
70
+ code: "UNAUTHORIZED",
71
+ statusCode: 401
72
+ });
73
+ this.name = "UnauthorizedError";
74
+ }
75
+ };
76
+ var ForbiddenError = class extends ArcError {
77
+ constructor(message = "Access denied") {
78
+ super(message, {
79
+ code: "FORBIDDEN",
80
+ statusCode: 403
81
+ });
82
+ this.name = "ForbiddenError";
83
+ }
84
+ };
85
+ var ConflictError = class extends ArcError {
86
+ constructor(message, field) {
87
+ super(message, {
88
+ code: "CONFLICT",
89
+ statusCode: 409,
90
+ details: field ? { field } : void 0
91
+ });
92
+ this.name = "ConflictError";
93
+ }
94
+ };
95
+ var OrgRequiredError = class extends ArcError {
96
+ organizations;
97
+ constructor(message, organizations) {
98
+ super(message, {
99
+ code: "ORG_SELECTION_REQUIRED",
100
+ statusCode: 403,
101
+ details: organizations ? { organizations } : void 0
102
+ });
103
+ this.name = "OrgRequiredError";
104
+ this.organizations = organizations;
105
+ }
106
+ };
107
+ var OrgAccessDeniedError = class extends ArcError {
108
+ constructor(orgId) {
109
+ super("Organization access denied", {
110
+ code: "ORG_ACCESS_DENIED",
111
+ statusCode: 403,
112
+ details: orgId ? { organizationId: orgId } : void 0
113
+ });
114
+ this.name = "OrgAccessDeniedError";
115
+ }
116
+ };
117
+ var RateLimitError = class extends ArcError {
118
+ retryAfter;
119
+ constructor(message = "Too many requests", retryAfter) {
120
+ super(message, {
121
+ code: "RATE_LIMITED",
122
+ statusCode: 429,
123
+ details: retryAfter ? { retryAfter } : void 0
124
+ });
125
+ this.name = "RateLimitError";
126
+ this.retryAfter = retryAfter;
127
+ }
128
+ };
129
+ var ServiceUnavailableError = class extends ArcError {
130
+ constructor(message = "Service temporarily unavailable") {
131
+ super(message, {
132
+ code: "SERVICE_UNAVAILABLE",
133
+ statusCode: 503
134
+ });
135
+ this.name = "ServiceUnavailableError";
136
+ }
137
+ };
138
+ function createError(statusCode, message, details) {
139
+ const codes = {
140
+ 400: "BAD_REQUEST",
141
+ 401: "UNAUTHORIZED",
142
+ 403: "FORBIDDEN",
143
+ 404: "NOT_FOUND",
144
+ 409: "CONFLICT",
145
+ 429: "RATE_LIMITED",
146
+ 500: "INTERNAL_ERROR",
147
+ 503: "SERVICE_UNAVAILABLE"
148
+ };
149
+ return new ArcError(message, {
150
+ code: codes[statusCode] ?? "ERROR",
151
+ statusCode,
152
+ details
153
+ });
154
+ }
155
+ function isArcError(error) {
156
+ return error instanceof ArcError;
157
+ }
158
+
159
+ // src/utils/responseSchemas.ts
160
+ var successResponseSchema = {
161
+ type: "object",
162
+ properties: {
163
+ success: { type: "boolean", example: true }
164
+ },
165
+ required: ["success"]
166
+ };
167
+ var errorResponseSchema = {
168
+ type: "object",
169
+ properties: {
170
+ success: { type: "boolean", example: false },
171
+ error: { type: "string", description: "Error message" },
172
+ code: { type: "string", description: "Error code" },
173
+ message: { type: "string", description: "Detailed message" }
174
+ },
175
+ required: ["success", "error"]
176
+ };
177
+ var paginationSchema = {
178
+ type: "object",
179
+ properties: {
180
+ page: { type: "integer", example: 1 },
181
+ limit: { type: "integer", example: 20 },
182
+ total: { type: "integer", example: 100 },
183
+ totalPages: { type: "integer", example: 5 },
184
+ hasNextPage: { type: "boolean", example: true },
185
+ hasPrevPage: { type: "boolean", example: false }
186
+ },
187
+ required: ["page", "limit", "total", "totalPages", "hasNextPage", "hasPrevPage"]
188
+ };
189
+ function wrapResponse(dataSchema) {
190
+ return {
191
+ type: "object",
192
+ properties: {
193
+ success: { type: "boolean", example: true },
194
+ data: dataSchema
195
+ },
196
+ required: ["success", "data"]
197
+ };
198
+ }
199
+ function listResponse(itemSchema) {
200
+ return {
201
+ type: "object",
202
+ properties: {
203
+ success: { type: "boolean", example: true },
204
+ data: {
205
+ type: "array",
206
+ items: itemSchema
207
+ },
208
+ pagination: paginationSchema
209
+ },
210
+ required: ["success", "data"]
211
+ };
212
+ }
213
+ function itemResponse(itemSchema) {
214
+ return wrapResponse(itemSchema);
215
+ }
216
+ function mutationResponse(itemSchema) {
217
+ return {
218
+ type: "object",
219
+ properties: {
220
+ success: { type: "boolean", example: true },
221
+ data: itemSchema,
222
+ message: { type: "string", example: "Created successfully" }
223
+ },
224
+ required: ["success", "data"]
225
+ };
226
+ }
227
+ function deleteResponse() {
228
+ return {
229
+ type: "object",
230
+ properties: {
231
+ success: { type: "boolean", example: true },
232
+ message: { type: "string", example: "Deleted successfully" }
233
+ },
234
+ required: ["success"]
235
+ };
236
+ }
237
+ var responses = {
238
+ 200: (schema) => ({
239
+ description: "Successful response",
240
+ content: {
241
+ "application/json": { schema }
242
+ }
243
+ }),
244
+ 201: (schema) => ({
245
+ description: "Created successfully",
246
+ content: {
247
+ "application/json": { schema: mutationResponse(schema) }
248
+ }
249
+ }),
250
+ 400: {
251
+ description: "Bad Request",
252
+ content: {
253
+ "application/json": {
254
+ schema: {
255
+ ...errorResponseSchema,
256
+ properties: {
257
+ ...errorResponseSchema.properties,
258
+ code: { type: "string", example: "VALIDATION_ERROR" },
259
+ details: {
260
+ type: "object",
261
+ properties: {
262
+ errors: {
263
+ type: "array",
264
+ items: {
265
+ type: "object",
266
+ properties: {
267
+ field: { type: "string" },
268
+ message: { type: "string" }
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
277
+ }
278
+ },
279
+ 401: {
280
+ description: "Unauthorized",
281
+ content: {
282
+ "application/json": {
283
+ schema: {
284
+ ...errorResponseSchema,
285
+ properties: {
286
+ ...errorResponseSchema.properties,
287
+ code: { type: "string", example: "UNAUTHORIZED" }
288
+ }
289
+ }
290
+ }
291
+ }
292
+ },
293
+ 403: {
294
+ description: "Forbidden",
295
+ content: {
296
+ "application/json": {
297
+ schema: {
298
+ ...errorResponseSchema,
299
+ properties: {
300
+ ...errorResponseSchema.properties,
301
+ code: { type: "string", example: "FORBIDDEN" }
302
+ }
303
+ }
304
+ }
305
+ }
306
+ },
307
+ 404: {
308
+ description: "Not Found",
309
+ content: {
310
+ "application/json": {
311
+ schema: {
312
+ ...errorResponseSchema,
313
+ properties: {
314
+ ...errorResponseSchema.properties,
315
+ code: { type: "string", example: "NOT_FOUND" }
316
+ }
317
+ }
318
+ }
319
+ }
320
+ },
321
+ 409: {
322
+ description: "Conflict",
323
+ content: {
324
+ "application/json": {
325
+ schema: {
326
+ ...errorResponseSchema,
327
+ properties: {
328
+ ...errorResponseSchema.properties,
329
+ code: { type: "string", example: "CONFLICT" }
330
+ }
331
+ }
332
+ }
333
+ }
334
+ },
335
+ 500: {
336
+ description: "Internal Server Error",
337
+ content: {
338
+ "application/json": {
339
+ schema: {
340
+ ...errorResponseSchema,
341
+ properties: {
342
+ ...errorResponseSchema.properties,
343
+ code: { type: "string", example: "INTERNAL_ERROR" }
344
+ }
345
+ }
346
+ }
347
+ }
348
+ }
349
+ };
350
+ var queryParams = {
351
+ pagination: {
352
+ page: {
353
+ type: "integer",
354
+ minimum: 1,
355
+ default: 1,
356
+ description: "Page number"
357
+ },
358
+ limit: {
359
+ type: "integer",
360
+ minimum: 1,
361
+ maximum: 100,
362
+ default: 20,
363
+ description: "Items per page"
364
+ }
365
+ },
366
+ sorting: {
367
+ sort: {
368
+ type: "string",
369
+ description: "Sort field (prefix with - for descending)",
370
+ example: "-createdAt"
371
+ }
372
+ },
373
+ filtering: {
374
+ select: {
375
+ type: "string",
376
+ description: "Fields to include (space-separated)",
377
+ example: "name email createdAt"
378
+ },
379
+ populate: {
380
+ type: "string",
381
+ description: "Relations to populate (comma-separated)",
382
+ example: "author,category"
383
+ }
384
+ }
385
+ };
386
+ function getListQueryParams() {
387
+ return {
388
+ type: "object",
389
+ properties: {
390
+ ...queryParams.pagination,
391
+ ...queryParams.sorting,
392
+ ...queryParams.filtering
393
+ }
394
+ };
395
+ }
396
+
397
+ // src/utils/stateMachine.ts
398
+ function createStateMachine(name, transitions = {}, options = {}) {
399
+ const normalized = /* @__PURE__ */ new Map();
400
+ const history = options.trackHistory ? [] : void 0;
401
+ Object.entries(transitions).forEach(([action, allowed]) => {
402
+ if (Array.isArray(allowed)) {
403
+ normalized.set(action, { from: new Set(allowed) });
404
+ } else if (typeof allowed === "object" && "from" in allowed) {
405
+ normalized.set(action, {
406
+ from: new Set(Array.isArray(allowed.from) ? allowed.from : [allowed.from]),
407
+ to: allowed.to,
408
+ guard: allowed.guard,
409
+ before: allowed.before,
410
+ after: allowed.after
411
+ });
412
+ }
413
+ });
414
+ const canSync = (action, status) => {
415
+ const transition = normalized.get(action);
416
+ if (!transition || !status) return false;
417
+ return transition.from.has(status);
418
+ };
419
+ const assert = (action, status, errorFactory, message) => {
420
+ if (canSync(action, status)) return;
421
+ const errorMessage = message || `${name} cannot '${action}' when status is '${status || "unknown"}'`;
422
+ if (typeof errorFactory === "function") {
423
+ throw errorFactory(errorMessage);
424
+ }
425
+ throw new Error(errorMessage);
426
+ };
427
+ const recordTransition = (from, to, action, metadata) => {
428
+ if (history) {
429
+ history.push({
430
+ from,
431
+ to,
432
+ action,
433
+ timestamp: /* @__PURE__ */ new Date(),
434
+ metadata
435
+ });
436
+ }
437
+ };
438
+ const getHistory = () => {
439
+ return history ? [...history] : [];
440
+ };
441
+ const clearHistory = () => {
442
+ if (history) {
443
+ history.length = 0;
444
+ }
445
+ };
446
+ const getAvailableActions = (status) => {
447
+ const actions = [];
448
+ for (const [action, transition] of normalized.entries()) {
449
+ if (transition.from.has(status)) {
450
+ actions.push(action);
451
+ }
452
+ }
453
+ return actions;
454
+ };
455
+ return {
456
+ can: canSync,
457
+ // Return sync version for backward compatibility
458
+ assert,
459
+ recordTransition,
460
+ getHistory,
461
+ clearHistory,
462
+ getAvailableActions
463
+ };
464
+ }
465
+
466
+ // src/utils/circuitBreaker.ts
467
+ var CircuitState = /* @__PURE__ */ ((CircuitState2) => {
468
+ CircuitState2["CLOSED"] = "CLOSED";
469
+ CircuitState2["OPEN"] = "OPEN";
470
+ CircuitState2["HALF_OPEN"] = "HALF_OPEN";
471
+ return CircuitState2;
472
+ })(CircuitState || {});
473
+ var CircuitBreakerError = class extends Error {
474
+ constructor(message, state) {
475
+ super(message);
476
+ this.state = state;
477
+ this.name = "CircuitBreakerError";
478
+ }
479
+ };
480
+ var CircuitBreaker = class {
481
+ constructor(fn, options = {}) {
482
+ this.fn = fn;
483
+ this.failureThreshold = options.failureThreshold ?? 5;
484
+ this.resetTimeout = options.resetTimeout ?? 6e4;
485
+ this.timeout = options.timeout ?? 1e4;
486
+ this.successThreshold = options.successThreshold ?? 1;
487
+ this.fallback = options.fallback;
488
+ this.onStateChange = options.onStateChange;
489
+ this.onError = options.onError;
490
+ this.name = options.name ?? "CircuitBreaker";
491
+ }
492
+ state = "CLOSED" /* CLOSED */;
493
+ failures = 0;
494
+ successes = 0;
495
+ totalCalls = 0;
496
+ nextAttempt = 0;
497
+ lastCallAt = null;
498
+ openedAt = null;
499
+ failureThreshold;
500
+ resetTimeout;
501
+ timeout;
502
+ successThreshold;
503
+ fallback;
504
+ onStateChange;
505
+ onError;
506
+ name;
507
+ /**
508
+ * Call the wrapped function with circuit breaker protection
509
+ */
510
+ async call(...args) {
511
+ this.totalCalls++;
512
+ this.lastCallAt = Date.now();
513
+ if (this.state === "OPEN" /* OPEN */) {
514
+ if (Date.now() < this.nextAttempt) {
515
+ const error = new CircuitBreakerError(
516
+ `Circuit breaker is OPEN for ${this.name}`,
517
+ "OPEN" /* OPEN */
518
+ );
519
+ if (this.fallback) {
520
+ return this.fallback(...args);
521
+ }
522
+ throw error;
523
+ }
524
+ this.setState("HALF_OPEN" /* HALF_OPEN */);
525
+ }
526
+ try {
527
+ const result = await this.executeWithTimeout(args);
528
+ this.onSuccess();
529
+ return result;
530
+ } catch (error) {
531
+ this.onFailure(error);
532
+ throw error;
533
+ }
534
+ }
535
+ /**
536
+ * Execute function with timeout
537
+ */
538
+ async executeWithTimeout(args) {
539
+ return new Promise((resolve, reject) => {
540
+ const timeoutId = setTimeout(() => {
541
+ reject(new Error(`Request timeout after ${this.timeout}ms`));
542
+ }, this.timeout);
543
+ this.fn(...args).then((result) => {
544
+ clearTimeout(timeoutId);
545
+ resolve(result);
546
+ }).catch((error) => {
547
+ clearTimeout(timeoutId);
548
+ reject(error);
549
+ });
550
+ });
551
+ }
552
+ /**
553
+ * Handle successful call
554
+ */
555
+ onSuccess() {
556
+ this.failures = 0;
557
+ this.successes++;
558
+ if (this.state === "HALF_OPEN" /* HALF_OPEN */) {
559
+ if (this.successes >= this.successThreshold) {
560
+ this.setState("CLOSED" /* CLOSED */);
561
+ this.successes = 0;
562
+ }
563
+ }
564
+ }
565
+ /**
566
+ * Handle failed call
567
+ */
568
+ onFailure(error) {
569
+ this.failures++;
570
+ this.successes = 0;
571
+ if (this.onError) {
572
+ this.onError(error);
573
+ }
574
+ if (this.state === "HALF_OPEN" /* HALF_OPEN */ || this.failures >= this.failureThreshold) {
575
+ this.setState("OPEN" /* OPEN */);
576
+ this.nextAttempt = Date.now() + this.resetTimeout;
577
+ this.openedAt = Date.now();
578
+ }
579
+ }
580
+ /**
581
+ * Change circuit state
582
+ */
583
+ setState(newState) {
584
+ const oldState = this.state;
585
+ if (oldState !== newState) {
586
+ this.state = newState;
587
+ if (this.onStateChange) {
588
+ this.onStateChange(oldState, newState);
589
+ }
590
+ }
591
+ }
592
+ /**
593
+ * Manually open the circuit
594
+ */
595
+ open() {
596
+ this.setState("OPEN" /* OPEN */);
597
+ this.nextAttempt = Date.now() + this.resetTimeout;
598
+ this.openedAt = Date.now();
599
+ }
600
+ /**
601
+ * Manually close the circuit
602
+ */
603
+ close() {
604
+ this.failures = 0;
605
+ this.successes = 0;
606
+ this.setState("CLOSED" /* CLOSED */);
607
+ this.openedAt = null;
608
+ }
609
+ /**
610
+ * Get current statistics
611
+ */
612
+ getStats() {
613
+ return {
614
+ name: this.name,
615
+ state: this.state,
616
+ failures: this.failures,
617
+ successes: this.successes,
618
+ totalCalls: this.totalCalls,
619
+ openedAt: this.openedAt,
620
+ lastCallAt: this.lastCallAt
621
+ };
622
+ }
623
+ /**
624
+ * Get current state
625
+ */
626
+ getState() {
627
+ return this.state;
628
+ }
629
+ /**
630
+ * Check if circuit is open
631
+ */
632
+ isOpen() {
633
+ return this.state === "OPEN" /* OPEN */;
634
+ }
635
+ /**
636
+ * Check if circuit is closed
637
+ */
638
+ isClosed() {
639
+ return this.state === "CLOSED" /* CLOSED */;
640
+ }
641
+ /**
642
+ * Reset statistics
643
+ */
644
+ reset() {
645
+ this.failures = 0;
646
+ this.successes = 0;
647
+ this.totalCalls = 0;
648
+ this.lastCallAt = null;
649
+ this.openedAt = null;
650
+ this.setState("CLOSED" /* CLOSED */);
651
+ }
652
+ };
653
+ function createCircuitBreaker(fn, options) {
654
+ return new CircuitBreaker(fn, options);
655
+ }
656
+ var CircuitBreakerRegistry = class {
657
+ breakers = /* @__PURE__ */ new Map();
658
+ /**
659
+ * Register a circuit breaker
660
+ */
661
+ register(name, fn, options) {
662
+ const breaker = new CircuitBreaker(fn, { ...options, name });
663
+ this.breakers.set(name, breaker);
664
+ return breaker;
665
+ }
666
+ /**
667
+ * Get a circuit breaker by name
668
+ */
669
+ get(name) {
670
+ return this.breakers.get(name);
671
+ }
672
+ /**
673
+ * Get all breakers
674
+ */
675
+ getAll() {
676
+ return this.breakers;
677
+ }
678
+ /**
679
+ * Get statistics for all breakers
680
+ */
681
+ getAllStats() {
682
+ const stats = {};
683
+ for (const [name, breaker] of this.breakers.entries()) {
684
+ stats[name] = breaker.getStats();
685
+ }
686
+ return stats;
687
+ }
688
+ /**
689
+ * Reset all breakers
690
+ */
691
+ resetAll() {
692
+ for (const breaker of this.breakers.values()) {
693
+ breaker.reset();
694
+ }
695
+ }
696
+ /**
697
+ * Open all breakers
698
+ */
699
+ openAll() {
700
+ for (const breaker of this.breakers.values()) {
701
+ breaker.open();
702
+ }
703
+ }
704
+ /**
705
+ * Close all breakers
706
+ */
707
+ closeAll() {
708
+ for (const breaker of this.breakers.values()) {
709
+ breaker.close();
710
+ }
711
+ }
712
+ };
713
+ var circuitBreakerRegistry = new CircuitBreakerRegistry();
714
+
715
+ // src/utils/queryParser.ts
716
+ var DANGEROUS_REGEX_PATTERNS = /(\{[0-9,]+\}|\*\+|\+\+|\?\+|(\(.+\))\+|\(\?\:|\\[0-9]|(\[.+\]).+(\[.+\]))/;
717
+ var MAX_REGEX_LENGTH = 500;
718
+ var MAX_SEARCH_LENGTH = 200;
719
+ var MAX_FILTER_DEPTH = 10;
720
+ var MAX_LIMIT = 1e3;
721
+ var DEFAULT_LIMIT = 20;
722
+ var ArcQueryParser = class {
723
+ maxLimit;
724
+ defaultLimit;
725
+ maxRegexLength;
726
+ maxSearchLength;
727
+ maxFilterDepth;
728
+ /** Supported filter operators */
729
+ operators = {
730
+ eq: "$eq",
731
+ ne: "$ne",
732
+ gt: "$gt",
733
+ gte: "$gte",
734
+ lt: "$lt",
735
+ lte: "$lte",
736
+ in: "$in",
737
+ nin: "$nin",
738
+ like: "$regex",
739
+ contains: "$regex",
740
+ regex: "$regex",
741
+ exists: "$exists"
742
+ };
743
+ constructor(options = {}) {
744
+ this.maxLimit = options.maxLimit ?? MAX_LIMIT;
745
+ this.defaultLimit = options.defaultLimit ?? DEFAULT_LIMIT;
746
+ this.maxRegexLength = options.maxRegexLength ?? MAX_REGEX_LENGTH;
747
+ this.maxSearchLength = options.maxSearchLength ?? MAX_SEARCH_LENGTH;
748
+ this.maxFilterDepth = options.maxFilterDepth ?? MAX_FILTER_DEPTH;
749
+ }
750
+ /**
751
+ * Parse URL query parameters into structured query options
752
+ */
753
+ parse(query) {
754
+ const q = query ?? {};
755
+ const page = this.parseNumber(q.page, 1);
756
+ const limit = Math.min(this.parseNumber(q.limit, this.defaultLimit), this.maxLimit);
757
+ const after = this.parseString(q.after ?? q.cursor);
758
+ const sort = this.parseSort(q.sort);
759
+ const populate = this.parseString(q.populate);
760
+ const search = this.parseSearch(q.search);
761
+ const select = this.parseSelect(q.select);
762
+ const filters = this.parseFilters(q);
763
+ return {
764
+ filters,
765
+ limit,
766
+ sort,
767
+ populate,
768
+ search,
769
+ page: after ? void 0 : page,
770
+ after,
771
+ select
772
+ };
773
+ }
774
+ // ============================================================================
775
+ // Parse Helpers
776
+ // ============================================================================
777
+ parseNumber(value, defaultValue) {
778
+ if (value === void 0 || value === null) return defaultValue;
779
+ const num = parseInt(String(value), 10);
780
+ return Number.isNaN(num) ? defaultValue : Math.max(1, num);
781
+ }
782
+ parseString(value) {
783
+ if (value === void 0 || value === null) return void 0;
784
+ const str = String(value).trim();
785
+ return str.length > 0 ? str : void 0;
786
+ }
787
+ parseSort(value) {
788
+ if (!value) return void 0;
789
+ const sortStr = String(value);
790
+ const result = {};
791
+ for (const field of sortStr.split(",")) {
792
+ const trimmed = field.trim();
793
+ if (!trimmed) continue;
794
+ if (!/^-?[a-zA-Z_][a-zA-Z0-9_.]*$/.test(trimmed)) continue;
795
+ if (trimmed.startsWith("-")) {
796
+ result[trimmed.slice(1)] = -1;
797
+ } else {
798
+ result[trimmed] = 1;
799
+ }
800
+ }
801
+ return Object.keys(result).length > 0 ? result : void 0;
802
+ }
803
+ parseSearch(value) {
804
+ if (!value) return void 0;
805
+ const search = String(value).trim();
806
+ if (search.length === 0) return void 0;
807
+ if (search.length > this.maxSearchLength) {
808
+ return search.slice(0, this.maxSearchLength);
809
+ }
810
+ return search;
811
+ }
812
+ parseSelect(value) {
813
+ if (!value) return void 0;
814
+ const selectStr = String(value);
815
+ const result = {};
816
+ for (const field of selectStr.split(",")) {
817
+ const trimmed = field.trim();
818
+ if (!trimmed) continue;
819
+ if (!/^-?[a-zA-Z_][a-zA-Z0-9_.]*$/.test(trimmed)) continue;
820
+ if (trimmed.startsWith("-")) {
821
+ result[trimmed.slice(1)] = 0;
822
+ } else {
823
+ result[trimmed] = 1;
824
+ }
825
+ }
826
+ return Object.keys(result).length > 0 ? result : void 0;
827
+ }
828
+ parseFilters(query) {
829
+ const reservedKeys = /* @__PURE__ */ new Set([
830
+ "page",
831
+ "limit",
832
+ "sort",
833
+ "populate",
834
+ "search",
835
+ "select",
836
+ "after",
837
+ "cursor",
838
+ "lean",
839
+ "_policyFilters"
840
+ ]);
841
+ const filters = {};
842
+ for (const [key, value] of Object.entries(query)) {
843
+ if (reservedKeys.has(key)) continue;
844
+ if (value === void 0 || value === null) continue;
845
+ const match = key.match(/^([a-zA-Z_][a-zA-Z0-9_.]*)(?:\[([a-z]+)\])?$/);
846
+ if (!match) continue;
847
+ const [, fieldName, operator] = match;
848
+ if (!fieldName) continue;
849
+ if (operator && this.operators[operator]) {
850
+ const mongoOp = this.operators[operator];
851
+ const parsedValue = this.parseFilterValue(value, operator);
852
+ if (!filters[fieldName]) {
853
+ filters[fieldName] = {};
854
+ }
855
+ filters[fieldName][mongoOp] = parsedValue;
856
+ } else if (!operator) {
857
+ filters[fieldName] = this.parseFilterValue(value);
858
+ }
859
+ }
860
+ return filters;
861
+ }
862
+ parseFilterValue(value, operator) {
863
+ if (operator === "in" || operator === "nin") {
864
+ if (Array.isArray(value)) {
865
+ return value.map((v) => this.coerceValue(v));
866
+ }
867
+ if (typeof value === "string" && value.includes(",")) {
868
+ return value.split(",").map((v) => this.coerceValue(v.trim()));
869
+ }
870
+ return [this.coerceValue(value)];
871
+ }
872
+ if (operator === "like" || operator === "contains" || operator === "regex") {
873
+ return this.sanitizeRegex(String(value));
874
+ }
875
+ if (operator === "exists") {
876
+ const str = String(value).toLowerCase();
877
+ return str === "true" || str === "1";
878
+ }
879
+ return this.coerceValue(value);
880
+ }
881
+ coerceValue(value) {
882
+ if (value === "true") return true;
883
+ if (value === "false") return false;
884
+ if (value === "null") return null;
885
+ if (typeof value === "string") {
886
+ const num = Number(value);
887
+ if (!Number.isNaN(num) && value.trim() !== "") {
888
+ return num;
889
+ }
890
+ }
891
+ return value;
892
+ }
893
+ sanitizeRegex(pattern) {
894
+ let sanitized = pattern.slice(0, this.maxRegexLength);
895
+ if (DANGEROUS_REGEX_PATTERNS.test(sanitized)) {
896
+ sanitized = sanitized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
897
+ }
898
+ return sanitized;
899
+ }
900
+ };
901
+ function createQueryParser(options) {
902
+ return new ArcQueryParser(options);
903
+ }
904
+
905
+ export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UnauthorizedError, ValidationError, circuitBreakerRegistry, createCircuitBreaker, createError, createQueryParser, createStateMachine, deleteResponse, errorResponseSchema, getListQueryParams, isArcError, itemResponse, listResponse, mutationResponse, paginationSchema, queryParams, responses, successResponseSchema, wrapResponse };