@stewardhq/sdk 0.1.0-rc.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 (48) hide show
  1. package/README.md +437 -0
  2. package/dist/_chunks/errors.js +73 -0
  3. package/dist/_chunks/events.d.ts +537 -0
  4. package/dist/_chunks/events.js +350 -0
  5. package/dist/_chunks/index.d.ts +6005 -0
  6. package/dist/_chunks/locale.d.ts +17 -0
  7. package/dist/_chunks/locale.js +17 -0
  8. package/dist/_chunks/src.js +1202 -0
  9. package/dist/_chunks/steward.d.ts +2588 -0
  10. package/dist/_chunks/steward.js +578 -0
  11. package/dist/_chunks/validators.d.ts +23 -0
  12. package/dist/_chunks/validators.js +106 -0
  13. package/dist/_chunks/webhook-core.d.ts +32 -0
  14. package/dist/_chunks/webhook-core.js +64 -0
  15. package/dist/_chunks/webhook.d.ts +22 -0
  16. package/dist/_chunks/webhook.js +53 -0
  17. package/dist/contract.d.ts +4 -0
  18. package/dist/contract.js +4 -0
  19. package/dist/index.d.ts +268 -0
  20. package/dist/index.js +784 -0
  21. package/dist/server.d.ts +236 -0
  22. package/dist/server.js +384 -0
  23. package/dist/testing/fixtures/events/LOCK.json +27 -0
  24. package/dist/testing/fixtures/events/account.deleted.json +36 -0
  25. package/dist/testing/fixtures/events/account.state_changed.json +53 -0
  26. package/dist/testing/fixtures/events/account.updated.json +37 -0
  27. package/dist/testing/fixtures/events/checkout.completed.json +36 -0
  28. package/dist/testing/fixtures/events/checkout.expired.json +26 -0
  29. package/dist/testing/fixtures/events/checkout.failed.json +27 -0
  30. package/dist/testing/fixtures/events/invoice.created.json +37 -0
  31. package/dist/testing/fixtures/events/invoice.issued.json +38 -0
  32. package/dist/testing/fixtures/events/invoice.voided.json +39 -0
  33. package/dist/testing/fixtures/events/subscription.activated.json +40 -0
  34. package/dist/testing/fixtures/events/subscription.cancel_scheduled.json +38 -0
  35. package/dist/testing/fixtures/events/subscription.canceled.json +29 -0
  36. package/dist/testing/fixtures/events/subscription.expired.json +27 -0
  37. package/dist/testing/fixtures/events/subscription.payment_failed.json +38 -0
  38. package/dist/testing/fixtures/events/subscription.reactivated.json +36 -0
  39. package/dist/testing/fixtures/events/subscription.renewed.json +39 -0
  40. package/dist/testing/fixtures/events/subscription.suspended.json +36 -0
  41. package/dist/testing/fixtures/events/subscription.terminated.json +28 -0
  42. package/dist/testing.d.ts +629 -0
  43. package/dist/testing.js +3759 -0
  44. package/dist/webhook/web.d.ts +17 -0
  45. package/dist/webhook/web.js +66 -0
  46. package/dist/webhook.d.ts +3 -0
  47. package/dist/webhook.js +3 -0
  48. package/package.json +70 -0
@@ -0,0 +1,350 @@
1
+ import { z } from "zod";
2
+ //#region ../contract/src/common.ts
3
+ const API_VERSION = "2026-10-01";
4
+ const CodeSchema = z.string().regex(/^[a-z][a-z0-9_]{0,62}$/, "küçük harf, rakam ve _ (harfle başlar)");
5
+ /** Ürün tarafındaki değişmez kimlik (ör. nixscale'de ağın shortId'si). */
6
+ const ExternalRefSchema = z.string().min(1).max(200).regex(/^[A-Za-z0-9_.:-]+$/, "yalnızca harf, rakam ve _ . : -");
7
+ const IdSchema = z.uuid();
8
+ const CurrencySchema = z.string().regex(/^[A-Z]{3}$/, "ISO-4217 kodu");
9
+ const AmountMinorSchema = z.number().int().nonnegative();
10
+ const IntervalSchema = z.enum(["month", "year"]);
11
+ const DateTimeSchema = z.iso.datetime({ offset: true });
12
+ /** Denetim izinde "kim yaptı": ör. `user:42`, `staff:7`, `system`. */
13
+ const ActorRefSchema = z.string().min(1).max(200).regex(/^[a-z]+(:[A-Za-z0-9_.@-]+)?$/, "tür[:kimlik]");
14
+ /**
15
+ * Entitlement değeri. `null` = sınırsız (limit türünde); boolean = özellik
16
+ * açık/kapalı; string = serbest ayar.
17
+ */
18
+ const EntitlementValueSchema = z.union([
19
+ z.number().int().nonnegative(),
20
+ z.boolean(),
21
+ z.string(),
22
+ z.null()
23
+ ]);
24
+ const EntitlementsSchema = z.record(CodeSchema, EntitlementValueSchema);
25
+ const MetadataSchema = z.record(z.string().max(64), z.string().max(500));
26
+ const ERROR_CATEGORIES = [
27
+ "validation",
28
+ "not_found",
29
+ "conflict",
30
+ "config",
31
+ "provider",
32
+ "unavailable"
33
+ ];
34
+ const ErrorCategorySchema = z.enum(ERROR_CATEGORIES);
35
+ /**
36
+ * Hata gövdesi. `error` makine kodu (açık küme; bilinenler `ERROR_CODES`),
37
+ * `message` insan için. `category`, `retryable` ve `requestId` 0.2'de eklendi;
38
+ * eski steward göndermez. Bilinmeyen (gelecekteki) bir kategori hatayı
39
+ * düşürmesin diye yok sayılır: kod her durumda okunur.
40
+ */
41
+ const ErrorResponseSchema = z.object({
42
+ error: z.string(),
43
+ message: z.string().optional(),
44
+ category: ErrorCategorySchema.optional().catch(void 0),
45
+ retryable: z.boolean().optional().catch(void 0),
46
+ requestId: z.string().optional().catch(void 0)
47
+ });
48
+ //#endregion
49
+ //#region ../contract/src/accounts.ts
50
+ const AccountUpsertInputSchema = z.object({
51
+ displayName: z.string().min(1).max(200),
52
+ metadata: MetadataSchema.default({})
53
+ });
54
+ const BillingProfileKindSchema = z.enum(["individual", "company"]);
55
+ const BillingProfileSchema = z.object({
56
+ kind: BillingProfileKindSchema,
57
+ contactName: z.string().min(2).max(200),
58
+ companyName: z.string().min(2).max(200).optional(),
59
+ taxOffice: z.string().min(2).max(100).optional(),
60
+ /** Kurumsal vergi kimliği (ör. VKN). Bireysel kimlik numarası profilde SAKLANMAZ. */
61
+ taxId: z.string().min(1).max(32).optional(),
62
+ email: z.email().max(200),
63
+ phone: z.string().regex(/^\+[1-9]\d{7,14}$/, "E.164 (+90…)"),
64
+ address: z.string().min(5).max(500),
65
+ city: z.string().min(2).max(100),
66
+ /** ISO-3166 alpha-2 */
67
+ country: z.string().regex(/^[A-Z]{2}$/).default("TR")
68
+ }).superRefine((p, ctx) => {
69
+ if (p.kind === "company") {
70
+ for (const key of [
71
+ "companyName",
72
+ "taxOffice",
73
+ "taxId"
74
+ ]) if (!p[key]) ctx.addIssue({
75
+ code: "custom",
76
+ path: [key],
77
+ message: "kurumsal profilde zorunlu"
78
+ });
79
+ }
80
+ });
81
+ const AccessStateSchema = z.enum(["active", "suspended"]);
82
+ const SubscriptionStatusSchema = z.enum([
83
+ "active",
84
+ "past_due",
85
+ "suspended",
86
+ "canceled",
87
+ "expired"
88
+ ]);
89
+ /** Snapshot içindeki canlı abonelik özeti (yoksa null). */
90
+ const SubscriptionSummarySchema = z.object({
91
+ id: IdSchema,
92
+ status: SubscriptionStatusSchema,
93
+ planCode: CodeSchema,
94
+ interval: IntervalSchema,
95
+ amountMinor: AmountMinorSchema,
96
+ currency: CurrencySchema,
97
+ currentPeriodEnd: DateTimeSchema.nullable(),
98
+ /** Dönem sonunda iptal planlandıysa iptal anı. */
99
+ cancelAt: DateTimeSchema.nullable()
100
+ });
101
+ const EntitlementSnapshotSchema = z.object({
102
+ version: z.number().int().nonnegative(),
103
+ planCode: CodeSchema,
104
+ accessState: AccessStateSchema,
105
+ entitlements: EntitlementsSchema,
106
+ subscription: SubscriptionSummarySchema.nullable()
107
+ });
108
+ /** Dunning'deki aboneliğin adımı. `step` tamamlanan politika adımı sayısı (başlangıçta 0). */
109
+ const AccountDunningStateSchema = z.object({
110
+ step: z.number().int().nonnegative(),
111
+ /** Sıradaki dunning aksiyonunun zamanı; kalmadıysa null. */
112
+ nextActionAt: DateTimeSchema.nullable()
113
+ });
114
+ /** Dönem sonunda uygulanacak plan değişikliği. F4b'ye kadar her zaman null. */
115
+ const AccountScheduledChangeSchema = z.object({
116
+ planCode: CodeSchema,
117
+ interval: IntervalSchema,
118
+ effectiveAt: DateTimeSchema
119
+ });
120
+ const AccountSubscriptionStateSchema = SubscriptionSummarySchema.extend({
121
+ cancelAtPeriodEnd: z.boolean(),
122
+ /** `past_due`'ya düştüğü an (dunning başlangıcı); ödeme sorunu yoksa null. */
123
+ pastDueSince: DateTimeSchema.nullable(),
124
+ currentPeriodStart: DateTimeSchema.nullable(),
125
+ /** Abonelik dunning'de değilse null. */
126
+ dunning: AccountDunningStateSchema.nullable(),
127
+ /** F4b (`POST /v1/subscriptions/:id/change`) gelene kadar her zaman null. */
128
+ scheduledChange: AccountScheduledChangeSchema.nullable()
129
+ });
130
+ /** Hesabın açık (tamamlanmamış, süresi dolmamış) checkout oturumu. */
131
+ const AccountOpenCheckoutSchema = z.object({
132
+ id: IdSchema,
133
+ planCode: CodeSchema,
134
+ interval: IntervalSchema,
135
+ intervalCount: z.number().int().min(1),
136
+ currency: CurrencySchema,
137
+ amountMinor: AmountMinorSchema,
138
+ expiresAt: DateTimeSchema
139
+ });
140
+ /** Fatura profili özeti: yalnızca var mı ve türü (alanlar PII, burada yok). */
141
+ const AccountProfileStateSchema = z.object({
142
+ present: z.boolean(),
143
+ kind: BillingProfileKindSchema.nullable()
144
+ });
145
+ const AccountStateSchema = EntitlementSnapshotSchema.extend({
146
+ ref: ExternalRefSchema,
147
+ displayName: z.string(),
148
+ /** Durumun hesaplandığı an. Değişiklik algılamaya katılmaz. */
149
+ updatedAt: DateTimeSchema,
150
+ /**
151
+ * Durumun hesaplandığı katalog sürümü — opak metin (steward `GET /v1/catalog`
152
+ * ETag değerini tırnaksız koyar); katalog yoksa null. Değişiklik algılamaya
153
+ * katılmaz; katalog önbelleğinin bayat olup olmadığını anlamak için.
154
+ */
155
+ catalogVersion: z.string().nullable(),
156
+ subscription: AccountSubscriptionStateSchema.nullable(),
157
+ openCheckout: AccountOpenCheckoutSchema.nullable(),
158
+ profile: AccountProfileStateSchema
159
+ });
160
+ const AccountSchema = z.object({
161
+ ref: ExternalRefSchema,
162
+ displayName: z.string(),
163
+ metadata: MetadataSchema,
164
+ createdAt: DateTimeSchema,
165
+ snapshot: EntitlementSnapshotSchema
166
+ });
167
+ const AccountDeleteResponseSchema = z.object({
168
+ ref: ExternalRefSchema,
169
+ deletedAt: DateTimeSchema
170
+ });
171
+ const ACCOUNT_INCLUDES = [
172
+ "profile",
173
+ "subscriptions",
174
+ "checkouts",
175
+ "payments",
176
+ "invoices",
177
+ "grants"
178
+ ];
179
+ //#endregion
180
+ //#region ../contract/src/events.ts
181
+ const EventIdSchema = z.string().regex(/^evt_[0-9a-f-]{36}$/);
182
+ const ApiVersionSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/);
183
+ const sub = { subscriptionId: IdSchema };
184
+ const EVENT_DETAIL_SCHEMAS = {
185
+ "account.updated": z.object({
186
+ /** Snapshot'ın hangi alanları değişti (planCode, accessState, entitlements, subscription). */
187
+ changed: z.array(z.string()) }),
188
+ "checkout.completed": z.object({
189
+ checkoutSessionId: IdSchema,
190
+ ...sub
191
+ }),
192
+ "checkout.failed": z.object({
193
+ checkoutSessionId: IdSchema,
194
+ reason: z.string()
195
+ }),
196
+ "checkout.expired": z.object({ checkoutSessionId: IdSchema }),
197
+ "subscription.activated": z.object({
198
+ ...sub,
199
+ planCode: CodeSchema,
200
+ interval: IntervalSchema,
201
+ amountMinor: AmountMinorSchema,
202
+ currency: CurrencySchema,
203
+ actorRef: z.string().nullable()
204
+ }),
205
+ "subscription.renewed": z.object({
206
+ ...sub,
207
+ paymentId: IdSchema,
208
+ periodStart: DateTimeSchema.nullable(),
209
+ periodEnd: DateTimeSchema.nullable(),
210
+ invoiceId: IdSchema.nullable()
211
+ }),
212
+ "subscription.payment_failed": z.object({
213
+ ...sub,
214
+ paymentId: IdSchema.nullable(),
215
+ /** Dunning'in kaçıncı başarısız denemesi (1'den başlar). */
216
+ attempt: z.number().int().positive(),
217
+ nextRetryAt: DateTimeSchema.nullable()
218
+ }),
219
+ "subscription.suspended": z.object({
220
+ ...sub,
221
+ dunningDay: z.number().int().nonnegative()
222
+ }),
223
+ "subscription.reactivated": z.object({
224
+ ...sub,
225
+ paymentId: IdSchema.nullable()
226
+ }),
227
+ "subscription.cancel_scheduled": z.object({
228
+ ...sub,
229
+ cancelAt: DateTimeSchema,
230
+ reason: z.string(),
231
+ actorRef: z.string().nullable()
232
+ }),
233
+ "subscription.canceled": z.object({
234
+ ...sub,
235
+ reason: z.string(),
236
+ effectiveAt: DateTimeSchema,
237
+ actorRef: z.string().nullable()
238
+ }),
239
+ "subscription.expired": z.object({
240
+ ...sub,
241
+ effectiveAt: DateTimeSchema
242
+ }),
243
+ "subscription.terminated": z.object({
244
+ ...sub,
245
+ reason: z.enum(["nonpayment"]),
246
+ dunningDay: z.number().int().nonnegative()
247
+ }),
248
+ "invoice.created": z.object({
249
+ invoiceId: IdSchema,
250
+ totalMinor: AmountMinorSchema,
251
+ currency: CurrencySchema
252
+ }),
253
+ "invoice.issued": z.object({
254
+ invoiceId: IdSchema,
255
+ number: z.string(),
256
+ totalMinor: AmountMinorSchema,
257
+ currency: CurrencySchema
258
+ }),
259
+ "invoice.voided": z.object({
260
+ invoiceId: IdSchema,
261
+ number: z.string().nullable(),
262
+ totalMinor: AmountMinorSchema,
263
+ currency: CurrencySchema,
264
+ reason: z.string()
265
+ }),
266
+ "account.state_changed": z.object({
267
+ cause: z.string().min(1).max(100),
268
+ /** Nedeni bir event ise onun id'si (aynı işlemde yayınlanan eski tip); iç nedende null. */
269
+ causeEventId: EventIdSchema.nullable()
270
+ }),
271
+ "account.deleted": z.object({
272
+ deletedAt: DateTimeSchema,
273
+ actorRef: z.string().nullable()
274
+ })
275
+ };
276
+ /** Tüm event tipleri (yenileri dahil). */
277
+ const EVENT_TYPES = Object.keys(EVENT_DETAIL_SCHEMAS);
278
+ /**
279
+ * 0.1 sözleşmesinin 15 tipi: `event_types` filtresi olmayan (NULL) endpoint'in
280
+ * aldığı küme. Değişmez; yeni tip buraya EKLENMEZ.
281
+ */
282
+ const LEGACY_EVENT_TYPES = Object.freeze([
283
+ "account.updated",
284
+ "checkout.completed",
285
+ "checkout.failed",
286
+ "checkout.expired",
287
+ "subscription.activated",
288
+ "subscription.renewed",
289
+ "subscription.payment_failed",
290
+ "subscription.suspended",
291
+ "subscription.reactivated",
292
+ "subscription.cancel_scheduled",
293
+ "subscription.canceled",
294
+ "subscription.expired",
295
+ "subscription.terminated",
296
+ "invoice.created",
297
+ "invoice.issued"
298
+ ]);
299
+ const FULL_STATE_EVENT_TYPES = /* @__PURE__ */ new Set(["account.state_changed", "account.deleted"]);
300
+ function accountSchemaOf(type) {
301
+ return FULL_STATE_EVENT_TYPES.has(type) ? AccountStateSchema : EntitlementSnapshotSchema;
302
+ }
303
+ function envelope(type) {
304
+ return z.object({
305
+ id: EventIdSchema,
306
+ type: z.literal(type),
307
+ apiVersion: ApiVersionSchema,
308
+ createdAt: DateTimeSchema,
309
+ product: CodeSchema,
310
+ accountRef: ExternalRefSchema,
311
+ data: z.object({
312
+ account: accountSchemaOf(type),
313
+ detail: EVENT_DETAIL_SCHEMAS[type]
314
+ })
315
+ });
316
+ }
317
+ const ENVELOPES = Object.fromEntries(EVENT_TYPES.map((t) => [t, envelope(t)]));
318
+ const BillingEventSchema = z.discriminatedUnion("type", Object.values(ENVELOPES));
319
+ function isEventType(event, type) {
320
+ return event.type === type;
321
+ }
322
+ /**
323
+ * Gevşek zarf: bu sürümün tanımadığı (gelecekteki) event tipini de kabul eder.
324
+ * Alıcı önce `BillingEventSchema` dener; tutmazsa bununla id/version/hesap
325
+ * alanlarını okuyup tekrarı ayıklar ve bilinmeyen tipi 2xx ile geçer (400
326
+ * dönmek teslimat sırasını tıkar). `data.account` ve `data.detail` doğrulanmaz.
327
+ */
328
+ const AnyBillingEventSchema = z.looseObject({
329
+ id: EventIdSchema,
330
+ type: z.string().min(1),
331
+ apiVersion: ApiVersionSchema,
332
+ createdAt: DateTimeSchema,
333
+ product: CodeSchema,
334
+ accountRef: ExternalRefSchema,
335
+ data: z.looseObject({
336
+ account: z.unknown(),
337
+ detail: z.unknown()
338
+ })
339
+ });
340
+ const PING_EVENT_TYPE = "endpoint.ping";
341
+ const PingSchema = z.object({
342
+ id: EventIdSchema,
343
+ type: z.literal(PING_EVENT_TYPE),
344
+ apiVersion: ApiVersionSchema,
345
+ createdAt: DateTimeSchema,
346
+ product: CodeSchema,
347
+ endpointId: IdSchema
348
+ });
349
+ //#endregion
350
+ export { CodeSchema as A, IntervalSchema as B, BillingProfileSchema as C, API_VERSION as D, SubscriptionSummarySchema as E, EntitlementsSchema as F, ErrorCategorySchema as I, ErrorResponseSchema as L, DateTimeSchema as M, ERROR_CATEGORIES as N, ActorRefSchema as O, EntitlementValueSchema as P, ExternalRefSchema as R, BillingProfileKindSchema as S, SubscriptionStatusSchema as T, MetadataSchema as V, AccountScheduledChangeSchema as _, EVENT_TYPES as a, AccountSubscriptionStateSchema as b, PING_EVENT_TYPE as c, ACCOUNT_INCLUDES as d, AccessStateSchema as f, AccountProfileStateSchema as g, AccountOpenCheckoutSchema as h, EVENT_DETAIL_SCHEMAS as i, CurrencySchema as j, AmountMinorSchema as k, PingSchema as l, AccountDunningStateSchema as m, ApiVersionSchema as n, EventIdSchema as o, AccountDeleteResponseSchema as p, BillingEventSchema as r, LEGACY_EVENT_TYPES as s, AnyBillingEventSchema as t, isEventType as u, AccountSchema as v, EntitlementSnapshotSchema as w, AccountUpsertInputSchema as x, AccountStateSchema as y, IdSchema as z };