@onesub/server 0.14.0 → 0.16.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 (140) hide show
  1. package/dist/__tests__/google-subscriptions-v2.test.d.ts +1 -1
  2. package/dist/__tests__/receipt-age.test.d.ts +7 -0
  3. package/dist/__tests__/receipt-age.test.d.ts.map +1 -0
  4. package/dist/__tests__/tracing.test.d.ts +2 -0
  5. package/dist/__tests__/tracing.test.d.ts.map +1 -0
  6. package/dist/cache.d.ts +5 -0
  7. package/dist/cache.d.ts.map +1 -1
  8. package/dist/index.cjs +178 -55
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.js +178 -55
  11. package/dist/index.js.map +1 -1
  12. package/dist/providers/apple.d.ts.map +1 -1
  13. package/dist/providers/google.d.ts.map +1 -1
  14. package/dist/providers/mock.d.ts.map +1 -1
  15. package/dist/routes/admin.d.ts.map +1 -1
  16. package/dist/routes/purchase.d.ts.map +1 -1
  17. package/dist/routes/validate.d.ts.map +1 -1
  18. package/dist/routes/webhook-apple.d.ts.map +1 -1
  19. package/dist/routes/webhook-google.d.ts +1 -1
  20. package/dist/routes/webhook-google.d.ts.map +1 -1
  21. package/dist/store.d.ts.map +1 -1
  22. package/dist/stores/postgres.d.ts.map +1 -1
  23. package/dist/stores/redis.d.ts +1 -0
  24. package/dist/stores/redis.d.ts.map +1 -1
  25. package/dist/tracing.d.ts.map +1 -1
  26. package/dist/webhook-events.d.ts +10 -0
  27. package/dist/webhook-events.d.ts.map +1 -1
  28. package/package.json +5 -3
  29. package/dist/__tests__/admin-customer-detail.test.js +0 -146
  30. package/dist/__tests__/admin-customer-detail.test.js.map +0 -1
  31. package/dist/__tests__/admin-list-subscriptions.test.js +0 -192
  32. package/dist/__tests__/admin-list-subscriptions.test.js.map +0 -1
  33. package/dist/__tests__/admin-subscription-detail.test.js +0 -112
  34. package/dist/__tests__/admin-subscription-detail.test.js.map +0 -1
  35. package/dist/__tests__/apple-root-ca.test.js +0 -41
  36. package/dist/__tests__/apple-root-ca.test.js.map +0 -1
  37. package/dist/__tests__/apple-status-api.test.js +0 -345
  38. package/dist/__tests__/apple-status-api.test.js.map +0 -1
  39. package/dist/__tests__/cache.test.js +0 -34
  40. package/dist/__tests__/cache.test.js.map +0 -1
  41. package/dist/__tests__/entitlements.test.js +0 -268
  42. package/dist/__tests__/entitlements.test.js.map +0 -1
  43. package/dist/__tests__/error-codes.test.js +0 -173
  44. package/dist/__tests__/error-codes.test.js.map +0 -1
  45. package/dist/__tests__/fetch-hardening.test.js +0 -192
  46. package/dist/__tests__/fetch-hardening.test.js.map +0 -1
  47. package/dist/__tests__/google-paused.test.js +0 -126
  48. package/dist/__tests__/google-paused.test.js.map +0 -1
  49. package/dist/__tests__/google-price-change.test.js +0 -186
  50. package/dist/__tests__/google-price-change.test.js.map +0 -1
  51. package/dist/__tests__/google-subscriptions-v2.test.js +0 -206
  52. package/dist/__tests__/google-subscriptions-v2.test.js.map +0 -1
  53. package/dist/__tests__/lifecycle-scenarios.test.js +0 -579
  54. package/dist/__tests__/lifecycle-scenarios.test.js.map +0 -1
  55. package/dist/__tests__/lifecycle-states.test.js +0 -423
  56. package/dist/__tests__/lifecycle-states.test.js.map +0 -1
  57. package/dist/__tests__/linked-purchase-token.test.js +0 -227
  58. package/dist/__tests__/linked-purchase-token.test.js.map +0 -1
  59. package/dist/__tests__/metrics.test.js +0 -340
  60. package/dist/__tests__/metrics.test.js.map +0 -1
  61. package/dist/__tests__/mock-provider.test.js +0 -188
  62. package/dist/__tests__/mock-provider.test.js.map +0 -1
  63. package/dist/__tests__/openapi.test.js +0 -32
  64. package/dist/__tests__/openapi.test.js.map +0 -1
  65. package/dist/__tests__/paused-auto-resume.test.js +0 -162
  66. package/dist/__tests__/paused-auto-resume.test.js.map +0 -1
  67. package/dist/__tests__/providers.test.js +0 -321
  68. package/dist/__tests__/providers.test.js.map +0 -1
  69. package/dist/__tests__/redis-store.test.js +0 -166
  70. package/dist/__tests__/redis-store.test.js.map +0 -1
  71. package/dist/__tests__/refund-policy.test.js +0 -317
  72. package/dist/__tests__/refund-policy.test.js.map +0 -1
  73. package/dist/__tests__/schema.test.js +0 -46
  74. package/dist/__tests__/schema.test.js.map +0 -1
  75. package/dist/__tests__/store.test.js +0 -172
  76. package/dist/__tests__/store.test.js.map +0 -1
  77. package/dist/__tests__/test-utils.js +0 -30
  78. package/dist/__tests__/test-utils.js.map +0 -1
  79. package/dist/__tests__/webhook-apple.test.js +0 -247
  80. package/dist/__tests__/webhook-apple.test.js.map +0 -1
  81. package/dist/__tests__/webhook-events.test.js +0 -36
  82. package/dist/__tests__/webhook-events.test.js.map +0 -1
  83. package/dist/__tests__/webhook-google.test.js +0 -255
  84. package/dist/__tests__/webhook-google.test.js.map +0 -1
  85. package/dist/__tests__/webhook-queue.test.js +0 -26
  86. package/dist/__tests__/webhook-queue.test.js.map +0 -1
  87. package/dist/__tests__/webhook-refund.test.js +0 -313
  88. package/dist/__tests__/webhook-refund.test.js.map +0 -1
  89. package/dist/cache.js +0 -39
  90. package/dist/cache.js.map +0 -1
  91. package/dist/errors.js +0 -19
  92. package/dist/errors.js.map +0 -1
  93. package/dist/http.js +0 -36
  94. package/dist/http.js.map +0 -1
  95. package/dist/logger.js +0 -20
  96. package/dist/logger.js.map +0 -1
  97. package/dist/openapi.js +0 -196
  98. package/dist/openapi.js.map +0 -1
  99. package/dist/providers/apple-root-ca.js +0 -38
  100. package/dist/providers/apple-root-ca.js.map +0 -1
  101. package/dist/providers/apple.js +0 -630
  102. package/dist/providers/apple.js.map +0 -1
  103. package/dist/providers/google.js +0 -564
  104. package/dist/providers/google.js.map +0 -1
  105. package/dist/providers/mock.js +0 -93
  106. package/dist/providers/mock.js.map +0 -1
  107. package/dist/routes/admin.js +0 -327
  108. package/dist/routes/admin.js.map +0 -1
  109. package/dist/routes/apple-offer.js +0 -72
  110. package/dist/routes/apple-offer.js.map +0 -1
  111. package/dist/routes/entitlements.js +0 -130
  112. package/dist/routes/entitlements.js.map +0 -1
  113. package/dist/routes/metrics.js +0 -281
  114. package/dist/routes/metrics.js.map +0 -1
  115. package/dist/routes/purchase.js +0 -204
  116. package/dist/routes/purchase.js.map +0 -1
  117. package/dist/routes/status.js +0 -55
  118. package/dist/routes/status.js.map +0 -1
  119. package/dist/routes/validate.js +0 -70
  120. package/dist/routes/validate.js.map +0 -1
  121. package/dist/routes/webhook-apple.js +0 -145
  122. package/dist/routes/webhook-apple.js.map +0 -1
  123. package/dist/routes/webhook-google.js +0 -204
  124. package/dist/routes/webhook-google.js.map +0 -1
  125. package/dist/routes/webhook.js +0 -19
  126. package/dist/routes/webhook.js.map +0 -1
  127. package/dist/store.js +0 -145
  128. package/dist/store.js.map +0 -1
  129. package/dist/stores/postgres.js +0 -329
  130. package/dist/stores/postgres.js.map +0 -1
  131. package/dist/stores/redis.js +0 -297
  132. package/dist/stores/redis.js.map +0 -1
  133. package/dist/stores/schema.js +0 -67
  134. package/dist/stores/schema.js.map +0 -1
  135. package/dist/tracing.js +0 -74
  136. package/dist/tracing.js.map +0 -1
  137. package/dist/webhook-events.js +0 -58
  138. package/dist/webhook-events.js.map +0 -1
  139. package/dist/webhook-queue.js +0 -108
  140. package/dist/webhook-queue.js.map +0 -1
@@ -1,317 +0,0 @@
1
- /**
2
- * Tests for OneSubServerConfig.refundPolicy.
3
- *
4
- * Covers:
5
- * - Default ('immediate'): subscription REFUND/REVOKE/voided sets status=canceled
6
- * - 'until_expiry': keeps status + expiresAt, only flips willRenew=false
7
- * - IAP refund is unaffected by refundPolicy (always immediate)
8
- * - status route's stale-record check (active && expiresAt > now)
9
- */
10
- import { describe, it, expect, beforeEach } from 'vitest';
11
- import express from 'express';
12
- import { SUBSCRIPTION_STATUS } from '@onesub/shared';
13
- import { createWebhookRouter } from '../routes/webhook.js';
14
- import { createStatusRouter } from '../routes/status.js';
15
- import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
16
- // ── helpers ─────────────────────────────────────────────────────────────────
17
- function makeJws(payload) {
18
- const header = Buffer.from(JSON.stringify({ alg: 'ES256' })).toString('base64url');
19
- const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
20
- return `${header}.${body}.fakesig`;
21
- }
22
- function spinUp(handler) {
23
- return {
24
- async request(method, path, body) {
25
- const server = handler.listen(0);
26
- const address = server.address();
27
- const port = typeof address === 'object' && address ? address.port : 0;
28
- try {
29
- const url = `http://127.0.0.1:${port}${path}`;
30
- const init = { method };
31
- if (body !== undefined) {
32
- init.headers = { 'Content-Type': 'application/json' };
33
- init.body = JSON.stringify(body);
34
- }
35
- const resp = await fetch(url, init);
36
- const text = await resp.text();
37
- let parsed = text;
38
- try {
39
- parsed = JSON.parse(text);
40
- }
41
- catch { /* keep as text */ }
42
- return { status: resp.status, body: parsed };
43
- }
44
- finally {
45
- await new Promise((resolve) => server.close(() => resolve()));
46
- }
47
- },
48
- };
49
- }
50
- function buildWebhookServer(config) {
51
- const store = new InMemorySubscriptionStore();
52
- const purchaseStore = new InMemoryPurchaseStore();
53
- const app = express();
54
- app.use(express.json());
55
- app.use(createWebhookRouter(config, store, purchaseStore));
56
- return { store, purchaseStore, server: spinUp(app) };
57
- }
58
- function buildStatusServer() {
59
- const store = new InMemorySubscriptionStore();
60
- const app = express();
61
- app.use(express.json());
62
- app.use(createStatusRouter(store));
63
- return { store, server: spinUp(app) };
64
- }
65
- const futureExpiry = '2099-01-01T00:00:00.000Z';
66
- const pastExpiry = '2024-01-01T00:00:00.000Z';
67
- const sampleSub = (overrides) => ({
68
- userId: 'user_x',
69
- productId: 'pro_monthly',
70
- platform: 'apple',
71
- status: 'active',
72
- expiresAt: futureExpiry,
73
- originalTransactionId: 'orig_x',
74
- purchasedAt: '2026-01-01T00:00:00.000Z',
75
- willRenew: true,
76
- ...overrides,
77
- });
78
- function appleSubRefundPayload(originalTransactionId) {
79
- const signedTransactionInfo = makeJws({
80
- bundleId: 'com.example.app',
81
- type: 'Auto-Renewable Subscription',
82
- productId: 'pro_monthly',
83
- transactionId: 'tx_refund',
84
- originalTransactionId,
85
- purchaseDate: Date.now() - 30 * 86400000,
86
- expiresDate: Date.now() + 30 * 86400000,
87
- });
88
- return {
89
- signedPayload: makeJws({
90
- notificationType: 'REFUND',
91
- data: { signedTransactionInfo, signedRenewalInfo: makeJws({}) },
92
- }),
93
- };
94
- }
95
- function appleConsumableRefundPayload(transactionId) {
96
- const signedTransactionInfo = makeJws({
97
- bundleId: 'com.example.app',
98
- type: 'Consumable',
99
- productId: 'credits_100',
100
- transactionId,
101
- originalTransactionId: transactionId,
102
- purchaseDate: Date.now(),
103
- });
104
- return {
105
- signedPayload: makeJws({
106
- notificationType: 'REFUND',
107
- data: { signedTransactionInfo, signedRenewalInfo: makeJws({}) },
108
- }),
109
- };
110
- }
111
- function googleVoidedSubPayload(purchaseToken) {
112
- const json = JSON.stringify({
113
- version: '1.0',
114
- packageName: 'com.example.app',
115
- eventTimeMillis: String(Date.now()),
116
- voidedPurchaseNotification: {
117
- purchaseToken,
118
- orderId: 'GPA.refunded',
119
- productType: 1,
120
- refundType: 1,
121
- },
122
- });
123
- return {
124
- message: { data: Buffer.from(json).toString('base64'), messageId: '1' },
125
- subscription: 's',
126
- };
127
- }
128
- const appleConfig = {
129
- bundleId: 'com.example.app',
130
- skipJwsVerification: true,
131
- };
132
- const googleConfig = {
133
- packageName: 'com.example.app',
134
- };
135
- // ── refundPolicy default ('immediate') ──────────────────────────────────────
136
- describe('refundPolicy default (immediate) — Apple subscription REFUND', () => {
137
- let store;
138
- let server;
139
- beforeEach(() => {
140
- const built = buildWebhookServer({ apple: appleConfig, database: { url: '' } });
141
- store = built.store;
142
- server = built.server;
143
- });
144
- it('marks status=canceled on REFUND', async () => {
145
- await store.save(sampleSub({ originalTransactionId: 'orig_immed' }));
146
- const resp = await server.request('POST', '/onesub/webhook/apple', appleSubRefundPayload('orig_immed'));
147
- expect(resp.status).toBe(200);
148
- expect((await store.getByTransactionId('orig_immed'))?.status).toBe(SUBSCRIPTION_STATUS.CANCELED);
149
- });
150
- });
151
- describe('refundPolicy default (immediate) — Google voided subscription', () => {
152
- it('marks status=canceled on voidedPurchaseNotification productType=1', async () => {
153
- const { store, server } = buildWebhookServer({ google: googleConfig, database: { url: '' } });
154
- await store.save(sampleSub({ platform: 'google', originalTransactionId: 'tok_immed' }));
155
- const resp = await server.request('POST', '/onesub/webhook/google', googleVoidedSubPayload('tok_immed'));
156
- expect(resp.status).toBe(200);
157
- expect((await store.getByTransactionId('tok_immed'))?.status).toBe(SUBSCRIPTION_STATUS.CANCELED);
158
- });
159
- });
160
- // ── refundPolicy 'until_expiry' ─────────────────────────────────────────────
161
- describe("refundPolicy 'until_expiry' — Apple subscription REFUND", () => {
162
- let store;
163
- let server;
164
- beforeEach(() => {
165
- const built = buildWebhookServer({
166
- apple: appleConfig,
167
- database: { url: '' },
168
- refundPolicy: 'until_expiry',
169
- });
170
- store = built.store;
171
- server = built.server;
172
- });
173
- it('keeps status=active and expiresAt; only flips willRenew=false', async () => {
174
- const original = sampleSub({
175
- originalTransactionId: 'orig_keep',
176
- status: 'active',
177
- expiresAt: futureExpiry,
178
- willRenew: true,
179
- });
180
- await store.save(original);
181
- await server.request('POST', '/onesub/webhook/apple', appleSubRefundPayload('orig_keep'));
182
- const updated = await store.getByTransactionId('orig_keep');
183
- expect(updated?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
184
- expect(updated?.expiresAt).toBe(futureExpiry);
185
- expect(updated?.willRenew).toBe(false);
186
- });
187
- it('still routes Apple REVOKE the same way (refund-class signal)', async () => {
188
- await store.save(sampleSub({ originalTransactionId: 'orig_revoke' }));
189
- const signedTransactionInfo = makeJws({
190
- bundleId: 'com.example.app',
191
- type: 'Auto-Renewable Subscription',
192
- productId: 'pro_monthly',
193
- transactionId: 'tx_revoke',
194
- originalTransactionId: 'orig_revoke',
195
- purchaseDate: Date.now(),
196
- expiresDate: Date.now() + 86400000,
197
- });
198
- const signedPayload = makeJws({
199
- notificationType: 'REVOKE',
200
- data: { signedTransactionInfo, signedRenewalInfo: makeJws({}) },
201
- });
202
- await server.request('POST', '/onesub/webhook/apple', { signedPayload });
203
- const updated = await store.getByTransactionId('orig_revoke');
204
- expect(updated?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
205
- expect(updated?.willRenew).toBe(false);
206
- });
207
- it('still applies normal expiry mapping for non-refund notifications (DID_RENEW)', async () => {
208
- await store.save(sampleSub({ originalTransactionId: 'orig_renew', willRenew: false }));
209
- const signedTransactionInfo = makeJws({
210
- bundleId: 'com.example.app',
211
- type: 'Auto-Renewable Subscription',
212
- productId: 'pro_monthly',
213
- transactionId: 'tx_renew',
214
- originalTransactionId: 'orig_renew',
215
- purchaseDate: Date.now(),
216
- expiresDate: Date.now() + 30 * 86400000,
217
- });
218
- const signedPayload = makeJws({
219
- notificationType: 'DID_RENEW',
220
- data: { signedTransactionInfo, signedRenewalInfo: makeJws({ autoRenewStatus: 1 }) },
221
- });
222
- await server.request('POST', '/onesub/webhook/apple', { signedPayload });
223
- const updated = await store.getByTransactionId('orig_renew');
224
- expect(updated?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
225
- // willRenew should reflect the renewal info (true), not stay false
226
- expect(updated?.willRenew).toBe(true);
227
- });
228
- });
229
- describe("refundPolicy 'until_expiry' — Google voided subscription", () => {
230
- it('keeps status=active and only flips willRenew=false', async () => {
231
- const { store, server } = buildWebhookServer({
232
- google: googleConfig,
233
- database: { url: '' },
234
- refundPolicy: 'until_expiry',
235
- });
236
- await store.save(sampleSub({
237
- platform: 'google',
238
- originalTransactionId: 'tok_keep',
239
- status: 'active',
240
- willRenew: true,
241
- }));
242
- await server.request('POST', '/onesub/webhook/google', googleVoidedSubPayload('tok_keep'));
243
- const updated = await store.getByTransactionId('tok_keep');
244
- expect(updated?.status).toBe(SUBSCRIPTION_STATUS.ACTIVE);
245
- expect(updated?.willRenew).toBe(false);
246
- });
247
- });
248
- // ── IAP refund is unaffected by refundPolicy ────────────────────────────────
249
- describe('refundPolicy does NOT apply to one-time IAP refunds', () => {
250
- const iap = (overrides) => ({
251
- userId: 'u',
252
- productId: 'credits_100',
253
- platform: 'apple',
254
- type: 'consumable',
255
- transactionId: 'iap_tx',
256
- purchasedAt: '2026-01-01T00:00:00.000Z',
257
- quantity: 1,
258
- ...overrides,
259
- });
260
- it("Apple IAP REFUND removes the row even when refundPolicy='until_expiry'", async () => {
261
- const { purchaseStore, server } = buildWebhookServer({
262
- apple: appleConfig,
263
- database: { url: '' },
264
- refundPolicy: 'until_expiry',
265
- });
266
- await purchaseStore.savePurchase(iap({ transactionId: 'iap_keep_test' }));
267
- await server.request('POST', '/onesub/webhook/apple', appleConsumableRefundPayload('iap_keep_test'));
268
- expect(await purchaseStore.getPurchaseByTransactionId('iap_keep_test')).toBeNull();
269
- });
270
- it("Google voided IAP (productType=2) deletes the row even when refundPolicy='until_expiry'", async () => {
271
- const { purchaseStore, server } = buildWebhookServer({
272
- google: googleConfig,
273
- database: { url: '' },
274
- refundPolicy: 'until_expiry',
275
- });
276
- await purchaseStore.savePurchase(iap({ platform: 'google', transactionId: 'GPA.iap_keep' }));
277
- const json = JSON.stringify({
278
- version: '1.0',
279
- packageName: 'com.example.app',
280
- eventTimeMillis: String(Date.now()),
281
- voidedPurchaseNotification: {
282
- purchaseToken: 'tok',
283
- orderId: 'GPA.iap_keep',
284
- productType: 2,
285
- refundType: 1,
286
- },
287
- });
288
- await server.request('POST', '/onesub/webhook/google', {
289
- message: { data: Buffer.from(json).toString('base64'), messageId: '1' },
290
- subscription: 's',
291
- });
292
- expect(await purchaseStore.getPurchaseByTransactionId('GPA.iap_keep')).toBeNull();
293
- });
294
- });
295
- // ── status route stale-record safety ────────────────────────────────────────
296
- describe('status route — stale active record', () => {
297
- it('returns active=false when status=active but expiresAt is in the past', async () => {
298
- const { store, server } = buildStatusServer();
299
- await store.save(sampleSub({ userId: 'u_stale', status: 'active', expiresAt: pastExpiry }));
300
- const resp = await server.request('GET', '/onesub/status?userId=u_stale');
301
- expect(resp.status).toBe(200);
302
- expect(resp.body.active).toBe(false);
303
- });
304
- it('returns active=true when status=active and expiresAt is in the future (normal case)', async () => {
305
- const { store, server } = buildStatusServer();
306
- await store.save(sampleSub({ userId: 'u_ok', status: 'active', expiresAt: futureExpiry }));
307
- const resp = await server.request('GET', '/onesub/status?userId=u_ok');
308
- expect(resp.body.active).toBe(true);
309
- });
310
- it('returns active=false for grace_period record past expiresAt', async () => {
311
- const { store, server } = buildStatusServer();
312
- await store.save(sampleSub({ userId: 'u_grace_stale', status: 'grace_period', expiresAt: pastExpiry }));
313
- const resp = await server.request('GET', '/onesub/status?userId=u_grace_stale');
314
- expect(resp.body.active).toBe(false);
315
- });
316
- });
317
- //# sourceMappingURL=refund-policy.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"refund-policy.test.js","sourceRoot":"","sources":["../../src/__tests__/refund-policy.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE/E,+EAA+E;AAE/E,SAAS,OAAO,CAAC,OAAgC;IAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACnF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACxE,OAAO,GAAG,MAAM,IAAI,IAAI,UAAU,CAAC;AACrC,CAAC;AAMD,SAAS,MAAM,CAAC,OAAwB;IACtC,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI;YAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,oBAAoB,IAAI,GAAG,IAAI,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,CAAC;gBACrC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACvB,IAAI,CAAC,OAAO,GAAG,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;oBACtD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBACnC,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBACpC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC/B,IAAI,MAAM,GAAY,IAAI,CAAC;gBAC3B,IAAI,CAAC;oBAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;gBAC/D,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAC/C,CAAC;oBAAS,CAAC;gBACT,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,MAA0B;IAKpD,MAAM,KAAK,GAAG,IAAI,yBAAyB,EAAE,CAAC;IAC9C,MAAM,aAAa,GAAG,IAAI,qBAAqB,EAAE,CAAC;IAClD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC;IAC3D,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;AACvD,CAAC;AAED,SAAS,iBAAiB;IACxB,MAAM,KAAK,GAAG,IAAI,yBAAyB,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACxB,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,YAAY,GAAG,0BAA0B,CAAC;AAChD,MAAM,UAAU,GAAG,0BAA0B,CAAC;AAE9C,MAAM,SAAS,GAAG,CAAC,SAAqC,EAAoB,EAAE,CAAC,CAAC;IAC9E,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,aAAa;IACxB,QAAQ,EAAE,OAAO;IACjB,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,YAAY;IACvB,qBAAqB,EAAE,QAAQ;IAC/B,WAAW,EAAE,0BAA0B;IACvC,SAAS,EAAE,IAAI;IACf,GAAG,SAAS;CACb,CAAC,CAAC;AAEH,SAAS,qBAAqB,CAAC,qBAA6B;IAC1D,MAAM,qBAAqB,GAAG,OAAO,CAAC;QACpC,QAAQ,EAAE,iBAAiB;QAC3B,IAAI,EAAE,6BAA6B;QACnC,SAAS,EAAE,aAAa;QACxB,aAAa,EAAE,WAAW;QAC1B,qBAAqB;QACrB,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ;QACxC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ;KACxC,CAAC,CAAC;IACH,OAAO;QACL,aAAa,EAAE,OAAO,CAAC;YACrB,gBAAgB,EAAE,QAAQ;YAC1B,IAAI,EAAE,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE;SAChE,CAAC;KACH,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CAAC,aAAqB;IACzD,MAAM,qBAAqB,GAAG,OAAO,CAAC;QACpC,QAAQ,EAAE,iBAAiB;QAC3B,IAAI,EAAE,YAAY;QAClB,SAAS,EAAE,aAAa;QACxB,aAAa;QACb,qBAAqB,EAAE,aAAa;QACpC,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;KACzB,CAAC,CAAC;IACH,OAAO;QACL,aAAa,EAAE,OAAO,CAAC;YACrB,gBAAgB,EAAE,QAAQ;YAC1B,IAAI,EAAE,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE;SAChE,CAAC;KACH,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,aAAqB;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,iBAAiB;QAC9B,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACnC,0BAA0B,EAAE;YAC1B,aAAa;YACb,OAAO,EAAE,cAAc;YACvB,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;SACd;KACF,CAAC,CAAC;IACH,OAAO;QACL,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE;QACvE,YAAY,EAAE,GAAG;KAClB,CAAC;AACJ,CAAC;AAED,MAAM,WAAW,GAA6C;IAC5D,QAAQ,EAAE,iBAAiB;IAC3B,mBAAmB,EAAE,IAAI;CAC1B,CAAC;AACF,MAAM,YAAY,GAA8C;IAC9D,WAAW,EAAE,iBAAiB;CAC/B,CAAC;AAEF,+EAA+E;AAE/E,QAAQ,CAAC,8DAA8D,EAAE,GAAG,EAAE;IAC5E,IAAI,KAAgC,CAAC;IACrC,IAAI,MAAkB,CAAC;IAEvB,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,KAAK,GAAG,kBAAkB,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAChF,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACpB,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,qBAAqB,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QACrE,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,uBAAuB,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC,CAAC;QACxG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+DAA+D,EAAE,GAAG,EAAE;IAC7E,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9F,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QAExF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,wBAAwB,EAAE,sBAAsB,CAAC,WAAW,CAAC,CAAC,CAAC;QACzG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACnG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,yDAAyD,EAAE,GAAG,EAAE;IACvE,IAAI,KAAgC,CAAC;IACrC,IAAI,MAAkB,CAAC;IAEvB,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,KAAK,GAAG,kBAAkB,CAAC;YAC/B,KAAK,EAAE,WAAW;YAClB,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YACrB,YAAY,EAAE,cAAc;SAC7B,CAAC,CAAC;QACH,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACpB,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,QAAQ,GAAG,SAAS,CAAC;YACzB,qBAAqB,EAAE,WAAW;YAClC,MAAM,EAAE,QAAQ;YAChB,SAAS,EAAE,YAAY;YACvB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE3B,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,uBAAuB,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC;QAE1F,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAC5D,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,qBAAqB,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;QAEtE,MAAM,qBAAqB,GAAG,OAAO,CAAC;YACpC,QAAQ,EAAE,iBAAiB;YAC3B,IAAI,EAAE,6BAA6B;YACnC,SAAS,EAAE,aAAa;YACxB,aAAa,EAAE,WAAW;YAC1B,qBAAqB,EAAE,aAAa;YACpC,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;YACxB,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;SACnC,CAAC,CAAC;QACH,MAAM,aAAa,GAAG,OAAO,CAAC;YAC5B,gBAAgB,EAAE,QAAQ;YAC1B,IAAI,EAAE,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE;SAChE,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,uBAAuB,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;QAEzE,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC;QAC9D,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,qBAAqB,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAEvF,MAAM,qBAAqB,GAAG,OAAO,CAAC;YACpC,QAAQ,EAAE,iBAAiB;YAC3B,IAAI,EAAE,6BAA6B;YACnC,SAAS,EAAE,aAAa;YACxB,aAAa,EAAE,UAAU;YACzB,qBAAqB,EAAE,YAAY;YACnC,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;YACxB,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,QAAQ;SACxC,CAAC,CAAC;QACH,MAAM,aAAa,GAAG,OAAO,CAAC;YAC5B,gBAAgB,EAAE,WAAW;YAC7B,IAAI,EAAE,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,EAAE;SACpF,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,uBAAuB,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC;QAEzE,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAC7D,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACzD,mEAAmE;QACnE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACxE,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC;YAC3C,MAAM,EAAE,YAAY;YACpB,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YACrB,YAAY,EAAE,cAAc;SAC7B,CAAC,CAAC;QACH,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;YACzB,QAAQ,EAAE,QAAQ;YAClB,qBAAqB,EAAE,UAAU;YACjC,MAAM,EAAE,QAAQ;YAChB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC,CAAC;QAEJ,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,wBAAwB,EAAE,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC;QAE3F,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACnE,MAAM,GAAG,GAAG,CAAC,SAAiC,EAAgB,EAAE,CAAC,CAAC;QAChE,MAAM,EAAE,GAAG;QACX,SAAS,EAAE,aAAa;QACxB,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,YAAY;QAClB,aAAa,EAAE,QAAQ;QACvB,WAAW,EAAE,0BAA0B;QACvC,QAAQ,EAAE,CAAC;QACX,GAAG,SAAS;KACb,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC;YACnD,KAAK,EAAE,WAAW;YAClB,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YACrB,YAAY,EAAE,cAAc;SAC7B,CAAC,CAAC;QACH,MAAM,aAAa,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;QAE1E,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,uBAAuB,EAAE,4BAA4B,CAAC,eAAe,CAAC,CAAC,CAAC;QAErG,MAAM,CAAC,MAAM,aAAa,CAAC,0BAA0B,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACrF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yFAAyF,EAAE,KAAK,IAAI,EAAE;QACvG,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC;YACnD,MAAM,EAAE,YAAY;YACpB,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YACrB,YAAY,EAAE,cAAc;SAC7B,CAAC,CAAC;QACH,MAAM,aAAa,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;QAE7F,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,iBAAiB;YAC9B,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACnC,0BAA0B,EAAE;gBAC1B,aAAa,EAAE,KAAK;gBACpB,OAAO,EAAE,cAAc;gBACvB,WAAW,EAAE,CAAC;gBACd,UAAU,EAAE,CAAC;aACd;SACF,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,wBAAwB,EAAE;YACrD,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE;YACvE,YAAY,EAAE,GAAG;SAClB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,aAAa,CAAC,0BAA0B,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACpF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAClD,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAC;QAC9C,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QAE5F,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,+BAA+B,CAAC,CAAC;QAC1E,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,CAAE,IAAI,CAAC,IAA4B,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;QACnG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAC;QAC9C,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QAE3F,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC;QACvE,MAAM,CAAE,IAAI,CAAC,IAA4B,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAC;QAC9C,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QAExG,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,qCAAqC,CAAC,CAAC;QAChF,MAAM,CAAE,IAAI,CAAC,IAA4B,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,46 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { fileURLToPath } from 'node:url';
3
- import { dirname, join } from 'node:path';
4
- import { describe, it, expect } from 'vitest';
5
- import { SUBSCRIPTIONS_SCHEMA_SQL, PURCHASES_SCHEMA_SQL } from '../stores/schema.js';
6
- /**
7
- * Parity check between the canonical SQL file (sql/schema.sql) and the
8
- * embedded string constants used by initSchema() at runtime. If this fails,
9
- * one of the two was edited without the other.
10
- */
11
- describe('schema SQL parity', () => {
12
- const here = dirname(fileURLToPath(import.meta.url));
13
- // __tests__ lives under src/, so sql/schema.sql is three levels up.
14
- const schemaPath = join(here, '..', '..', 'sql', 'schema.sql');
15
- const sqlFile = readFileSync(schemaPath, 'utf8');
16
- // Normalize both sides to compare by semantics rather than whitespace:
17
- // strip SQL line comments, collapse whitespace, lowercase.
18
- // The leading \r normalisation matters on Windows checkouts (core.autocrlf):
19
- // without it, `--.*$` fails to match because `$` doesn't see `\r` as a line
20
- // terminator, leaving comments in place and breaking the parity check.
21
- const normalize = (sql) => sql
22
- .replace(/\r/g, '')
23
- .split('\n')
24
- .map((l) => l.replace(/--.*$/, ''))
25
- .join('\n')
26
- .replace(/\s+/g, ' ')
27
- .trim()
28
- .toLowerCase();
29
- it('contains both table DDLs in the canonical .sql file', () => {
30
- const n = normalize(sqlFile);
31
- expect(n).toContain('create table if not exists onesub_subscriptions');
32
- expect(n).toContain('create table if not exists onesub_purchases');
33
- });
34
- it('subscriptions DDL constant appears verbatim in schema.sql', () => {
35
- expect(normalize(sqlFile)).toContain(normalize(SUBSCRIPTIONS_SCHEMA_SQL));
36
- });
37
- it('purchases DDL constant appears verbatim in schema.sql', () => {
38
- expect(normalize(sqlFile)).toContain(normalize(PURCHASES_SCHEMA_SQL));
39
- });
40
- it('non-consumable partial unique index is present (0.2.0 semantics)', () => {
41
- const n = normalize(sqlFile);
42
- expect(n).toContain('create unique index if not exists idx_onesub_purchases_non_consumable');
43
- expect(n).toContain("where type = 'non_consumable'");
44
- });
45
- });
46
- //# sourceMappingURL=schema.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../../src/__tests__/schema.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAErF;;;;GAIG;AACH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,oEAAoE;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAEjD,uEAAuE;IACvE,2DAA2D;IAC3D,6EAA6E;IAC7E,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE,CAChC,GAAG;SACA,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;SAClC,IAAI,CAAC,IAAI,CAAC;SACV,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE;SACN,WAAW,EAAE,CAAC;IAEnB,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,iDAAiD,CAAC,CAAC;QACvE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,6CAA6C,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,uEAAuE,CAAC,CAAC;QAC7F,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,172 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { InMemorySubscriptionStore, InMemoryPurchaseStore } from '../store.js';
3
- const makeSub = (overrides) => ({
4
- userId: 'user_123',
5
- productId: 'com.example.pro_monthly',
6
- platform: 'apple',
7
- status: 'active',
8
- expiresAt: '2025-12-31T00:00:00.000Z',
9
- originalTransactionId: 'txn_abc123',
10
- purchasedAt: '2025-01-01T00:00:00.000Z',
11
- willRenew: true,
12
- ...overrides,
13
- });
14
- describe('InMemorySubscriptionStore', () => {
15
- let store;
16
- beforeEach(() => {
17
- store = new InMemorySubscriptionStore();
18
- });
19
- it('save() and getByUserId() returns the correct subscription', async () => {
20
- const sub = makeSub();
21
- await store.save(sub);
22
- const result = await store.getByUserId('user_123');
23
- expect(result).toEqual(sub);
24
- });
25
- it('save() and getByTransactionId() returns the correct subscription', async () => {
26
- const sub = makeSub();
27
- await store.save(sub);
28
- const result = await store.getByTransactionId('txn_abc123');
29
- expect(result).toEqual(sub);
30
- });
31
- it('getByUserId() returns null for an unknown user', async () => {
32
- const result = await store.getByUserId('nonexistent_user');
33
- expect(result).toBeNull();
34
- });
35
- it('getByTransactionId() returns null for an unknown transaction', async () => {
36
- const result = await store.getByTransactionId('nonexistent_txn');
37
- expect(result).toBeNull();
38
- });
39
- it('save() overwrites an existing subscription for the same userId', async () => {
40
- const original = makeSub({ status: 'active', expiresAt: '2025-06-01T00:00:00.000Z' });
41
- await store.save(original);
42
- const updated = makeSub({
43
- status: 'expired',
44
- expiresAt: '2025-01-15T00:00:00.000Z',
45
- originalTransactionId: 'txn_abc123',
46
- });
47
- await store.save(updated);
48
- const result = await store.getByUserId('user_123');
49
- expect(result?.status).toBe('expired');
50
- expect(result?.expiresAt).toBe('2025-01-15T00:00:00.000Z');
51
- });
52
- it('save() overwrites an existing subscription indexed by the same transactionId', async () => {
53
- const original = makeSub({ willRenew: true });
54
- await store.save(original);
55
- const updated = makeSub({ willRenew: false, originalTransactionId: 'txn_abc123' });
56
- await store.save(updated);
57
- const result = await store.getByTransactionId('txn_abc123');
58
- expect(result?.willRenew).toBe(false);
59
- });
60
- it('stores multiple subscriptions independently', async () => {
61
- const sub1 = makeSub({ userId: 'user_1', originalTransactionId: 'txn_1' });
62
- const sub2 = makeSub({ userId: 'user_2', originalTransactionId: 'txn_2', platform: 'google' });
63
- await store.save(sub1);
64
- await store.save(sub2);
65
- expect(await store.getByUserId('user_1')).toEqual(sub1);
66
- expect(await store.getByUserId('user_2')).toEqual(sub2);
67
- expect(await store.getByTransactionId('txn_1')).toEqual(sub1);
68
- expect(await store.getByTransactionId('txn_2')).toEqual(sub2);
69
- });
70
- });
71
- // ── InMemoryPurchaseStore ────────────────────────────────────────────────────
72
- const makePurchase = (overrides) => ({
73
- userId: 'user_1',
74
- productId: 'credits_10',
75
- platform: 'apple',
76
- type: 'consumable',
77
- transactionId: 'txn_p1',
78
- purchasedAt: '2026-01-01T00:00:00.000Z',
79
- quantity: 1,
80
- ...overrides,
81
- });
82
- describe('InMemoryPurchaseStore', () => {
83
- let purchaseStore;
84
- beforeEach(() => {
85
- purchaseStore = new InMemoryPurchaseStore();
86
- });
87
- it('savePurchase + getPurchasesByUserId returns purchases', async () => {
88
- const p = makePurchase();
89
- await purchaseStore.savePurchase(p);
90
- const results = await purchaseStore.getPurchasesByUserId('user_1');
91
- expect(results).toHaveLength(1);
92
- expect(results[0]).toEqual(p);
93
- });
94
- it('getPurchaseByTransactionId returns the correct purchase', async () => {
95
- const p = makePurchase();
96
- await purchaseStore.savePurchase(p);
97
- expect(await purchaseStore.getPurchaseByTransactionId('txn_p1')).toEqual(p);
98
- expect(await purchaseStore.getPurchaseByTransactionId('unknown')).toBeNull();
99
- });
100
- it('hasPurchased returns true for existing purchase', async () => {
101
- await purchaseStore.savePurchase(makePurchase({ type: 'non_consumable', productId: 'premium_unlock' }));
102
- expect(await purchaseStore.hasPurchased('user_1', 'premium_unlock')).toBe(true);
103
- expect(await purchaseStore.hasPurchased('user_1', 'other_product')).toBe(false);
104
- expect(await purchaseStore.hasPurchased('user_2', 'premium_unlock')).toBe(false);
105
- });
106
- it('stores multiple consumable purchases for same user+product', async () => {
107
- await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_1' }));
108
- await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_2' }));
109
- await purchaseStore.savePurchase(makePurchase({ transactionId: 'txn_3' }));
110
- const results = await purchaseStore.getPurchasesByUserId('user_1');
111
- expect(results).toHaveLength(3);
112
- });
113
- it('getPurchasesByUserId returns empty for unknown user', async () => {
114
- expect(await purchaseStore.getPurchasesByUserId('unknown')).toEqual([]);
115
- });
116
- it('stores purchases for different users independently', async () => {
117
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
118
- await purchaseStore.savePurchase(makePurchase({ userId: 'b', transactionId: 't2' }));
119
- expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
120
- expect(await purchaseStore.getPurchasesByUserId('b')).toHaveLength(1);
121
- });
122
- it('savePurchase is idempotent for same transactionId + same userId', async () => {
123
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
124
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
125
- expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
126
- });
127
- it('savePurchase rejects same transactionId from a different userId', async () => {
128
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
129
- await expect(purchaseStore.savePurchase(makePurchase({ userId: 'b', transactionId: 't1' }))).rejects.toMatchObject({ code: 'TRANSACTION_BELONGS_TO_OTHER_USER' });
130
- // Original owner unchanged
131
- expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
132
- expect(await purchaseStore.getPurchasesByUserId('b')).toHaveLength(0);
133
- });
134
- it('reassignPurchase transfers ownership to a new userId', async () => {
135
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }));
136
- expect(await purchaseStore.reassignPurchase('t1', 'b')).toBe(true);
137
- expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(0);
138
- expect(await purchaseStore.getPurchasesByUserId('b')).toHaveLength(1);
139
- // now saving as 'b' is idempotent, 'a' is rejected
140
- await purchaseStore.savePurchase(makePurchase({ userId: 'b', transactionId: 't1' }));
141
- await expect(purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1' }))).rejects.toMatchObject({ code: 'TRANSACTION_BELONGS_TO_OTHER_USER' });
142
- });
143
- it('reassignPurchase returns false for unknown transaction', async () => {
144
- expect(await purchaseStore.reassignPurchase('nope', 'a')).toBe(false);
145
- });
146
- it('deletePurchases removes matching rows', async () => {
147
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1', productId: 'pass' }));
148
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't2', productId: 'pass' }));
149
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't3', productId: 'other' }));
150
- const deleted = await purchaseStore.deletePurchases('a', 'pass');
151
- expect(deleted).toBe(2);
152
- expect(await purchaseStore.getPurchasesByUserId('a')).toHaveLength(1);
153
- });
154
- it('deletePurchaseByTransactionId removes only the matching row', async () => {
155
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't1', productId: 'coins' }));
156
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 't2', productId: 'coins' }));
157
- expect(await purchaseStore.deletePurchaseByTransactionId('t1')).toBe(true);
158
- const remaining = await purchaseStore.getPurchasesByUserId('a');
159
- expect(remaining).toHaveLength(1);
160
- expect(remaining[0].transactionId).toBe('t2');
161
- expect(await purchaseStore.getPurchaseByTransactionId('t1')).toBeNull();
162
- });
163
- it('deletePurchaseByTransactionId returns false for unknown transaction', async () => {
164
- expect(await purchaseStore.deletePurchaseByTransactionId('nope')).toBe(false);
165
- });
166
- it('deletePurchaseByTransactionId removes the user index when last row is gone', async () => {
167
- await purchaseStore.savePurchase(makePurchase({ userId: 'a', transactionId: 'only' }));
168
- expect(await purchaseStore.deletePurchaseByTransactionId('only')).toBe(true);
169
- expect(await purchaseStore.getPurchasesByUserId('a')).toEqual([]);
170
- });
171
- });
172
- //# sourceMappingURL=store.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../src/__tests__/store.test.ts"],"names":[],"mappings":"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"}
@@ -1,30 +0,0 @@
1
- /**
2
- * Shared test helpers — kept here so unit tests don't reinvent the wheel
3
- * (and don't trip CodeQL with substring URL matching, which is unsafe in
4
- * production code and inconsistent across files).
5
- */
6
- /**
7
- * Extract the hostname from a fetch input (string | URL | Request).
8
- * Returns null when the input isn't a parseable absolute URL.
9
- *
10
- * Use this instead of `urlStr.includes('host.com')` — substring matching
11
- * matches `https://attacker.example/?q=host.com` too, which CodeQL flags.
12
- */
13
- export function urlHost(input) {
14
- try {
15
- return new URL(String(input)).hostname;
16
- }
17
- catch {
18
- return null;
19
- }
20
- }
21
- /**
22
- * True for fetch URLs targeting the in-process test HTTP server. Used by
23
- * fetch mocks that need to pass-through the test runner's own requests
24
- * while intercepting outbound API calls.
25
- */
26
- export function isLocalhostUrl(input) {
27
- const host = urlHost(input);
28
- return host === '127.0.0.1' || host === 'localhost';
29
- }
30
- //# sourceMappingURL=test-utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../src/__tests__/test-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,CAAC;AACtD,CAAC"}