@stewardhq/sdk 0.3.0 → 0.6.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 (41) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +332 -273
  3. package/dist/_chunks/errors.js +1 -1
  4. package/dist/_chunks/events.d.ts +386 -78
  5. package/dist/_chunks/events.js +1035 -19
  6. package/dist/_chunks/index.d.ts +3752 -305
  7. package/dist/_chunks/locale.d.ts +270 -2
  8. package/dist/_chunks/src.js +925 -31
  9. package/dist/_chunks/validators.d.ts +46 -2
  10. package/dist/_chunks/validators.js +128 -27
  11. package/dist/_chunks/webhook-core.d.ts +2 -2
  12. package/dist/contract.d.ts +4 -4
  13. package/dist/contract.js +4 -4
  14. package/dist/index.d.ts +447 -33
  15. package/dist/index.js +1659 -26
  16. package/dist/server.d.ts +66 -16
  17. package/dist/server.js +35 -4
  18. package/package.json +14 -13
  19. package/dist/_chunks/steward.d.ts +0 -189
  20. package/dist/_chunks/steward.js +0 -564
  21. package/dist/testing/fixtures/events/LOCK.json +0 -27
  22. package/dist/testing/fixtures/events/account.deleted.json +0 -36
  23. package/dist/testing/fixtures/events/account.state_changed.json +0 -53
  24. package/dist/testing/fixtures/events/account.updated.json +0 -37
  25. package/dist/testing/fixtures/events/checkout.completed.json +0 -36
  26. package/dist/testing/fixtures/events/checkout.expired.json +0 -26
  27. package/dist/testing/fixtures/events/checkout.failed.json +0 -27
  28. package/dist/testing/fixtures/events/invoice.created.json +0 -37
  29. package/dist/testing/fixtures/events/invoice.issued.json +0 -38
  30. package/dist/testing/fixtures/events/invoice.voided.json +0 -39
  31. package/dist/testing/fixtures/events/subscription.activated.json +0 -40
  32. package/dist/testing/fixtures/events/subscription.cancel_scheduled.json +0 -38
  33. package/dist/testing/fixtures/events/subscription.canceled.json +0 -29
  34. package/dist/testing/fixtures/events/subscription.expired.json +0 -27
  35. package/dist/testing/fixtures/events/subscription.payment_failed.json +0 -38
  36. package/dist/testing/fixtures/events/subscription.reactivated.json +0 -36
  37. package/dist/testing/fixtures/events/subscription.renewed.json +0 -39
  38. package/dist/testing/fixtures/events/subscription.suspended.json +0 -36
  39. package/dist/testing/fixtures/events/subscription.terminated.json +0 -28
  40. package/dist/testing.d.ts +0 -656
  41. package/dist/testing.js +0 -3684
package/dist/index.js CHANGED
@@ -1,12 +1,640 @@
1
- import { $ as CurrencySchema, A as EMAIL_LOCALES, B as NotificationEmailsSchema, C as BillingProfileKindSchema, D as SubscriptionSummarySchema, E as SubscriptionStatusSchema, F as EmailSettingsPatchSchema, G as emailTemplateOf, H as billingPageUrl, I as EmailSettingsSchema, J as uniqueEmails, K as emailTemplateSettings, L as EmailTemplateSchema, M as EMAIL_TEMPLATE_BY_EVENT, N as EmailExtraTextSchema, O as BILLING_PATH_PLACEHOLDERS, P as EmailSenderSchema, Q as CodeSchema, R as EmailTemplateSettingsSchema, S as AccountUpsertInputSchema, T as EntitlementSnapshotSchema, U as emailLocaleOf, V as applyEmailSettingsPatch, W as emailRecipients, X as ActorRefSchema, Y as API_VERSION, Z as AmountMinorSchema, _ as AccountProfileStateSchema, a as EVENT_TYPES, at as ErrorResponseSchema, b as AccountStateSchema, c as PING_EVENT_TYPE, ct as IntervalSchema, d as ACCOUNT_INCLUDES, et as DateTimeSchema, f as AccessStateSchema, g as AccountOpenCheckoutSchema, h as AccountLocaleSchema, i as EVENT_DETAIL_SCHEMAS, it as ErrorCategorySchema, j as EMAIL_TEMPLATES, k as BillingPathSchema, l as PingSchema, lt as MetadataSchema, m as AccountDunningStateSchema, n as ApiVersionSchema, nt as EntitlementValueSchema, o as EventIdSchema, ot as ExternalRefSchema, p as AccountDeleteResponseSchema, q as isEmailRelevant, r as BillingEventSchema, rt as EntitlementsSchema, s as LEGACY_EVENT_TYPES, st as IdSchema, t as AnyBillingEventSchema, tt as ERROR_CATEGORIES, u as isEventType, v as AccountScheduledChangeSchema, w as BillingProfileSchema, x as AccountSubscriptionStateSchema, y as AccountSchema, z as MAX_NOTIFICATION_EMAILS } from "./_chunks/events.js";
2
- import { $ as InvoiceVoidResponseSchema, $t as PriceSchema, A as WebhookSecretSchema, At as checkoutRedirectUrl, B as errorCodeSpec, Bt as TtlMinutesSchema, C as WEBHOOK_SECRET_PREFIX, Ct as CheckoutSessionCreateInputSchema, D as WebhookEndpointSchema, Dt as ConsentSchema, E as WebhookEndpointRotateInputSchema, Et as CheckoutStatusSchema, F as PlanSetModeSchema, Ft as ProductSettingsPatchSchema, G as CheckoutSessionResponseSchema, Gt as AccountsRefreshResultSchema, H as AccountDetailSchema, Ht as applySettingsPatch, I as PlanSetResponseSchema, It as ProductSettingsSchema, J as HEADER_API_VERSION, Jt as CatalogSyncResultSchema, K as GrantCreateResponseSchema, Kt as CatalogSchema, L as PlanSetWarningSchema, Lt as ReturnOriginSchema, M as webhookEndpointUrlProblem, Mt as BrandingSchema, N as PLAN_SET_MODES, Nt as DEFAULT_CHECKOUT_TTL_MINUTES, O as WebhookEndpointStatusSchema, Ot as MAX_CHECKOUT_CONSENT_DOCS, P as PlanSetInputSchema, Pt as DEFAULT_PORTAL_TTL_MINUTES, Q as InvoiceIssueResponseSchema, Qt as PriceDefSchema, R as ResyncResultSchema, Rt as SETTINGS_LOCALES, S as WEBHOOK_ENDPOINT_STATUSES, St as CheckoutPrefillSchema, T as WebhookEndpointListSchema, Tt as CheckoutSessionSchema, U as AccountUpsertResponseSchema, Ut as isReturnUrlAllowed, V as isKnownErrorCode, Vt as WebUrlSchema, W as CancelSubscriptionResponseSchema, Wt as returnOriginsOf, X as HEADER_REQUEST_ID, Xt as FeatureTypeSchema, Y as HEADER_IDEMPOTENCY_KEY, Yt as FeatureDefSchema, Z as HEADER_SDK, Zt as PlanDefSchema, _ as emailReadinessOf, _t as PORTAL_SESSION_ERRORS, a as DOCTOR_SEVERITIES, at as InvoiceVoidInputSchema, b as DEFAULT_ENDPOINT_EVENT_TYPES, bt as CHECKOUT_ERRORS, c as DoctorEndpointSchema, ct as ManualInvoiceLineInputSchema, d as DoctorReportSchema, dt as CancelSubscriptionInputSchema, et as ManualInvoiceResponseSchema, f as DoctorSettingsSchema, ft as GrantCreateInputSchema, g as buildDoctorReport, gt as SubscriptionSchema, h as DoctorWarningSchema, ht as StatsSchema, i as DOCTOR_REQUIRED_LEGAL_LINKS, it as InvoiceStatusSchema, j as generateWebhookSecret, jt as BrandingPatchSchema, k as WebhookLastDeliverySchema, kt as ProviderPayloadSchema, l as DoctorHostedSchema, lt as manualInvoiceTotals, m as DoctorUnsellablePriceSchema, mt as PaymentSchema, n as DOCTOR_PROVIDER_ENVIRONMENTS, nt as InvoiceLineSchema, o as DoctorCatalogSchema, ot as MANUAL_INVOICE_MAX_AMOUNT_MINOR, p as DoctorSeveritySchema, pt as GrantSchema, q as HEADER_ACTOR_REF, qt as CatalogSyncInputSchema, r as DOCTOR_REQUIRED_BRANDING, rt as InvoiceSchema, s as DoctorEmailsSchema, st as ManualInvoiceInputSchema, t as DOCTOR_PENDING_BACKLOG_MS, tt as InvoiceIssueInputSchema, u as DoctorProviderSchema, ut as splitInclusiveTax, v as endpointDiagnosisOf, vt as PortalSessionCreateInputSchema, w as WebhookEndpointCreateInputSchema, wt as CheckoutSessionCreatedSchema, x as EventTypeSchema, xt as CheckoutConsentInputSchema, y as hostedReadinessOf, yt as PortalSessionSchema, z as ERROR_CODES, zt as SettingsLocaleSchema } from "./_chunks/src.js";
3
- import { a as isGrantActive, i as LIVE_SUBSCRIPTION_STATUSES, n as isValidTckn, o as isLiveSubscriptionStatus, r as isValidVkn, s as resolveEntitlements, t as isE164 } from "./_chunks/validators.js";
4
- import { t as createSteward } from "./_chunks/steward.js";
1
+ import { $ as emailLocaleOf, $t as MeteredPriceSchema, A as SubscriptionStatusSchema, An as hasControlCharacters, At as METER_FILTER_MAX_CONDITIONS, B as EmailSenderSchema, Bn as EntitlementsSchema, Bt as MeterCreditEntryKindSchema, C as AccountSchema, Cn as EventNameSchema, Ct as DEFAULT_METER_LOW_AT, D as BillingProfileKindSchema, Dn as IngestEventSchema, Dt as METER_AGGREGATION_KINDS, E as AccountUpsertInputSchema, En as INGEST_REJECT_CODES, Et as MAX_METER_UNITS, F as EMAIL_LOCALES, Fn as CodeSchema, Ft as MeterAggregationSchema, G as InvoicePaymentDueEmailSchema, Gn as INVOICE_PAYMENT_STATUSES, Gt as MeterLabelSchema, H as EmailSettingsSchema, Hn as ErrorResponseSchema, Ht as MeterCreditPropertiesSchema, I as EMAIL_PAYLOAD_SCHEMAS, In as CurrencySchema, It as MeterChargeLineSchema, J as NotificationEmailsSchema, Jn as InvoiceBillingReasonSchema, Jt as MeterPeriodSchema, K as MAX_NOTIFICATION_EMAILS, Kn as IdSchema, Kt as MeterLowAtSchema, L as EMAIL_TEMPLATES, Ln as DateTimeSchema, Lt as MeterChargeListSchema, M as BILLING_PATH_PLACEHOLDERS, Mn as API_VERSION, Mt as METER_FILTER_OPERATORS, N as BankTransferInstructionsEmailSchema, Nn as ActorRefSchema, Nt as METER_PERIOD_STATUSES, O as BillingProfileSchema, On as IngestRejectCodeSchema, Ot as METER_CHARGE_STATUSES, P as BillingPathSchema, Pn as AmountMinorSchema, Pt as METER_STANDINGS, Q as billingPageUrl, Qt as MeteredPriceDefSchema, R as EMAIL_TEMPLATE_BY_EVENT, Rn as ERROR_CATEGORIES, Rt as MeterChargeSchema, S as AccountScheduledChangeSchema, Sn as EventMetadataValueSchema, St as CreditGrantInputSchema, T as AccountSubscriptionStateSchema, Tn as EventsIngestResultSchema, Tt as MAX_METERED_PRICE_PER_UNITS, U as EmailTemplateSchema, Un as ExternalRefSchema, Ut as MeterDefSchema, V as EmailSettingsPatchSchema, Vn as ErrorCategorySchema, Vt as MeterCreditEntrySchema, W as EmailTemplateSettingsSchema, Wn as INVOICE_BILLING_REASONS, Wt as MeterFilterSchema, X as PriceChangeNoticeMeteredRateSchema, Xn as MetadataSchema, Xt as MeterStandingSchema, Y as PriceChangeNoticeEmailSchema, Yn as InvoicePaymentStatusSchema, Yt as MeterPeriodStatusSchema, Z as applyEmailSettingsPatch, Zt as MeterUnitsSchema, _ as AccountMeterStateSchema, _n as EVENT_TIMESTAMP_MAX_AGE_DAYS, _t as isFeatureFlagBenefit, a as EVENT_TYPES, an as fromMicroUnits, at as uniqueEmails, b as AccountOpenInvoiceSchema, bn as EventMetadataKeySchema, bt as AccountMetersSchema, c as LEGACY_EVENT_TYPES, cn as parseMicroUnits, ct as BenefitDefSchema, d as isEventType, dn as EVENTS_INGEST_MAX_BYTES, dt as BenefitTypeSchema, en as canonicalMeterFilter, et as emailRecipients, f as ACCOUNT_INCLUDES, fn as EVENTS_INGEST_MAX_EVENTS, ft as CatalogBenefitSchema, g as AccountLocaleSchema, gn as EVENT_NAME_MAX_LENGTH, gt as featureValueMatchesType, h as AccountDunningStateSchema, hn as EVENT_METADATA_STRING_MAX_LENGTH, ht as MeterCreditBenefitDefSchema, i as EVENT_DETAIL_SCHEMAS, in as UNIT_DECIMALS, it as liveSubscriptionOf, j as SubscriptionSummarySchema, jn as isEventTimestampInRange, jt as METER_FILTER_MAX_DEPTH, k as EntitlementSnapshotSchema, kn as IngestRejectionSchema, kt as METER_CREDIT_ENTRY_KINDS, l as PING_EVENT_TYPE, ln as plainDecimalOf, lt as BenefitDescriptionSchema, m as AccountDeleteResponseSchema, mn as EVENT_METADATA_MAX_PAIRS, mt as FeatureFlagPropertiesSchema, n as ApiVersionSchema, nn as InvalidUnitValueError, nt as emailTemplateSettings, o as EventIdSchema, on as isMicroPrecise, ot as AccountBenefitSchema, p as AccessStateSchema, pn as EVENT_METADATA_KEY_MAX_LENGTH, pt as FeatureFlagBenefitDefSchema, q as NoticedMeteredRateSchema, qn as IntervalSchema, qt as MeterPeriodListSchema, r as BillingEventSchema, rn as MICRO_UNITS_PER_UNIT, rt as isEmailRelevant, s as FULL_STATE_EVENT_TYPES, sn as microToDecimalString, st as BENEFIT_TYPES, t as AnyBillingEventSchema, tn as meterFilterClauseOf, tt as emailTemplateOf, u as PingSchema, un as toMicroUnits, ut as BenefitSourceSchema, v as AccountMetersStateSchema, vn as EVENT_TIMESTAMP_MAX_SKEW_MINUTES, vt as isMeterCreditBenefit, w as AccountStateSchema, wn as EventsIngestInputSchema, wt as DecimalUnitsStringSchema, x as AccountProfileStateSchema, xn as EventMetadataSchema, xt as CatalogMeterSchema, y as AccountOpenCheckoutSchema, yn as EventExternalIdSchema, yt as AccountMeterSchema, z as EmailExtraTextSchema, zn as EntitlementValueSchema, zt as MeterChargeStatusSchema } from "./_chunks/events.js";
2
+ import { $ as rolloverOf, $n as MAX_CATALOG_BENEFITS, $t as CheckoutSessionCreatedSchema, A as WebhookEndpointRotateInputSchema, An as MeteringSettingsPatchSchema, At as InvoiceWaiveInputSchema, B as PlanSetResponseSchema, Bn as applySettingsPatch, Bt as PaymentSchema, C as hostedReadinessOf, Cn as DEFAULT_PORTAL_TTL_MINUTES, Ct as InvoiceIssueInputSchema, D as WEBHOOK_SECRET_PREFIX, Dn as LateGraceMinutesSchema, Dt as InvoiceSchema, E as WEBHOOK_ENDPOINT_STATUSES, En as EventsSettingsSchema, Et as InvoicePaymentSessionSchema, F as generateWebhookSecret, Fn as ReturnOriginSchema, Ft as splitInclusiveTax, G as contributionOf, Gn as returnOriginsOf, Gt as PortalSessionCreateInputSchema, H as ResyncResultSchema, Hn as eventsSettingsOf, Ht as SubscriptionChangeInputSchema, I as webhookEndpointUrlProblem, In as SETTINGS_LOCALES, It as CancelSubscriptionInputSchema, J as meterCreditsOf, Jn as CatalogSchema, Jt as BankTransferRecordedSchema, K as matchesFilter, Kn as AccountsRefreshResultSchema, Kt as PortalSessionSchema, L as PLAN_SET_MODES, Ln as SettingsLocaleSchema, Lt as GrantCreateInputSchema, M as WebhookEndpointStatusSchema, Mn as PaymentDueDaysSchema, Mt as ManualInvoiceInputSchema, N as WebhookLastDeliverySchema, Nn as ProductSettingsPatchSchema, Nt as ManualInvoiceLineInputSchema, O as WebhookEndpointCreateInputSchema, On as METER_CHARGE_MODES, Ot as InvoiceStatusSchema, P as WebhookSecretSchema, Pn as ProductSettingsSchema, Pt as manualInvoiceTotals, Q as rateOverage, Qn as FeatureTypeSchema, Qt as CheckoutSessionCreateInputSchema, R as PlanSetInputSchema, Rn as TtlMinutesSchema, Rt as GrantSchema, S as endpointDiagnosisOf, Sn as DEFAULT_PAYMENT_DUE_DAYS, St as UncancelSubscriptionResponseSchema, T as EventTypeSchema, Tn as EventsSettingsPatchSchema, Tt as InvoicePaymentSessionCreateInputSchema, U as addUtcMonths, Un as isReturnUrlAllowed, Ut as SubscriptionSchema, V as PlanSetWarningSchema, Vn as billingSettingsOf, Vt as StatsSchema, W as consumptionOf, Wn as meteringSettingsOf, Wt as PORTAL_SESSION_ERRORS, X as overageOf, Xn as CatalogSyncResultSchema, Xt as CheckoutConsentInputSchema, Y as overageCapReached, Yn as CatalogSyncInputSchema, Yt as CHECKOUT_ERRORS, Z as periodBoundsOf, Zn as FeatureDefSchema, Zt as CheckoutPrefillSchema, _ as DoctorSeveritySchema, _n as DEFAULT_CHECKOUT_TTL_MINUTES, _t as InvoiceVoidResponseSchema, a as DOCTOR_ROLLUP_LAG_WARN_SECONDS, an as PAYMENT_METHODS, ar as PriceDefSchema, at as AccountUpsertResponseSchema, b as buildDoctorReport, bn as DEFAULT_METER_CHARGE_MODE, bt as MeResponseSchema, c as DoctorCatalogSchema, cn as checkoutRedirectUrl, ct as CheckoutSessionResponseSchema, d as DoctorEventsSchema, dn as BankTransferSettingsSchema, dt as HEADER_ACTOR_REF, en as CheckoutSessionSchema, er as MAX_CATALOG_METERED_PRICES, et as standingOf, f as DoctorHostedSchema, fn as BillingSettingsPatchSchema, ft as HEADER_API_VERSION, g as DoctorSettingsSchema, gn as DEFAULT_BANK_TRANSFER_PAYMENT_DAYS, gt as InvoiceIssueResponseSchema, h as DoctorReportSchema, hn as BrandingSchema, ht as HEADER_SDK, i as DOCTOR_REQUIRED_LEGAL_LINKS, in as MAX_CHECKOUT_CONSENT_DOCS, ir as PlanDefSchema, it as AccountDetailSchema, j as WebhookEndpointSchema, jn as MeteringSettingsSchema, jt as MANUAL_INVOICE_MAX_AMOUNT_MINOR, k as WebhookEndpointListSchema, kn as MeterChargeModeSchema, kt as InvoiceVoidInputSchema, l as DoctorEmailsSchema, ln as BankTransferPaymentDaysSchema, lt as CreditGrantResponseSchema, m as DoctorProviderSchema, mn as BrandingPatchSchema, mt as HEADER_REQUEST_ID, n as DOCTOR_PROVIDER_ENVIRONMENTS, nn as ConsentSchema, nr as MAX_PLAN_BENEFITS, nt as errorCodeSpec, o as DOCTOR_SEVERITIES, on as PaymentMethodSchema, or as PriceSchema, ot as CancelSubscriptionResponseSchema, p as DoctorOverageSchema, pn as BillingSettingsSchema, pt as HEADER_IDEMPOTENCY_KEY, q as meterChargeTotals, qn as CatalogPlanSchema, qt as BankTransferRecordInputSchema, r as DOCTOR_REQUIRED_BRANDING, rn as INDIVIDUAL_IDENTITY_PLACEHOLDER, rr as METERED_PRICE_TAX_MISMATCH, rt as isKnownErrorCode, s as DoctorBillingSchema, sn as ProviderPayloadSchema, st as ChangeSubscriptionResponseSchema, t as DOCTOR_PENDING_BACKLOG_MS, tn as CheckoutStatusSchema, tr as MAX_CATALOG_METERS, tt as ERROR_CODES, u as DoctorEndpointSchema, un as BankTransferSettingsPatchSchema, ut as GrantCreateResponseSchema, v as DoctorUnsellablePriceSchema, vn as DEFAULT_EVENT_RETENTION_DAYS, vt as InvoiceWaiveResponseSchema, w as DEFAULT_ENDPOINT_EVENT_TYPES, wn as EventRetentionDaysSchema, wt as InvoiceLineSchema, x as emailReadinessOf, xn as DEFAULT_MIN_INVOICE_MINOR, xt as STEWARD_CAPABILITIES, y as DoctorWarningSchema, yn as DEFAULT_LATE_GRACE_MINUTES, yt as ManualInvoiceResponseSchema, z as PlanSetModeSchema, zn as WebUrlSchema, zt as MAX_GRANT_BENEFITS } from "./_chunks/src.js";
3
+ import { a as LIVE_SUBSCRIPTION_STATUSES, c as isLiveSubscriptionStatus, i as FEATURE_FLAG_BENEFIT_TYPE, l as resolveBenefits, n as isValidTckn, o as OVERRIDE_BENEFIT_SOURCE, r as isValidVkn, s as isGrantActive, t as isE164, u as resolveEntitlements } from "./_chunks/validators.js";
5
4
  import { a as StewardNetworkError, i as StewardError, n as StewardConfigError, r as StewardContractError, t as StewardApiError } from "./_chunks/errors.js";
5
+ //#region ../contract/src/client.ts
6
+ /**
7
+ * `Retry-After` başlığını milisaniyeye çevirir: saniye (tam sayı) ya da HTTP
8
+ * tarihi. Geçmiş tarih 0; okunamayan değer null.
9
+ */
10
+ function parseRetryAfter(value, now = Date.now()) {
11
+ if (value === null || value === void 0) return null;
12
+ const trimmed = value.trim();
13
+ if (/^\d+$/.test(trimmed)) return Number(trimmed) * 1e3;
14
+ if (!/^[A-Za-z]/.test(trimmed)) return null;
15
+ const at = Date.parse(trimmed);
16
+ return Number.isNaN(at) ? null : Math.max(0, at - now);
17
+ }
18
+ const SDK_HEADER_VALUE = /^[\x21-\x7e][\x20-\x7e]{0,199}$/;
19
+ function createBillingClient(options) {
20
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
21
+ const doFetch = options.fetch ?? fetch;
22
+ const timeoutMs = options.timeoutMs ?? 1e4;
23
+ if (options.sdk !== void 0 && !SDK_HEADER_VALUE.test(options.sdk)) throw new Error("sdk: yazdırılabilir ASCII, en çok 200 karakter");
24
+ function meta(res) {
25
+ const out = {};
26
+ const requestId = res.headers.get(HEADER_REQUEST_ID);
27
+ if (requestId) out.requestId = requestId;
28
+ if (res.status === 429 || res.status === 503) {
29
+ const retryAfterMs = parseRetryAfter(res.headers.get("retry-after"));
30
+ if (retryAfterMs !== null) out.retryAfterMs = retryAfterMs;
31
+ }
32
+ return out;
33
+ }
34
+ async function send(method, path, body, call, extraHeaders) {
35
+ const headers = {
36
+ authorization: `Bearer ${options.apiKey}`,
37
+ accept: "application/json",
38
+ [HEADER_API_VERSION]: API_VERSION,
39
+ ...extraHeaders
40
+ };
41
+ if (options.sdk !== void 0) headers[HEADER_SDK] = options.sdk;
42
+ if (method !== "GET") headers[HEADER_IDEMPOTENCY_KEY] = call?.idempotencyKey ?? globalThis.crypto.randomUUID();
43
+ if (call?.actorRef) headers[HEADER_ACTOR_REF] = call.actorRef;
44
+ if (body !== void 0) headers["content-type"] = "application/json";
45
+ try {
46
+ return { res: await doFetch(`${baseUrl}${path}`, {
47
+ method,
48
+ headers,
49
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {},
50
+ signal: AbortSignal.timeout(timeoutMs)
51
+ }) };
52
+ } catch (error) {
53
+ return {
54
+ ok: false,
55
+ status: 0,
56
+ error: error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError") ? "timeout" : "network_error"
57
+ };
58
+ }
59
+ }
60
+ async function failure(res) {
61
+ const json = await res.json().catch(() => null);
62
+ const parsed = ErrorResponseSchema.safeParse(json);
63
+ const { requestId, retryAfterMs } = meta(res);
64
+ const common = {
65
+ ...requestId !== void 0 ? { requestId } : {},
66
+ ...retryAfterMs !== void 0 ? { retryAfterMs } : {}
67
+ };
68
+ if (parsed.success) {
69
+ const { error, message, category, retryable } = parsed.data;
70
+ const bodyRequestId = requestId === void 0 ? parsed.data.requestId : void 0;
71
+ return {
72
+ ok: false,
73
+ status: res.status,
74
+ error,
75
+ ...message !== void 0 ? { message } : {},
76
+ ...category !== void 0 ? { category } : {},
77
+ ...retryable !== void 0 ? { retryable } : {},
78
+ ...bodyRequestId !== void 0 ? { requestId: bodyRequestId } : {},
79
+ ...common
80
+ };
81
+ }
82
+ return {
83
+ ok: false,
84
+ status: res.status,
85
+ error: `http_${res.status}`,
86
+ ...common
87
+ };
88
+ }
89
+ function success(res, data) {
90
+ const { requestId } = meta(res);
91
+ return {
92
+ ok: true,
93
+ status: res.status,
94
+ data,
95
+ ...requestId !== void 0 ? { requestId } : {}
96
+ };
97
+ }
98
+ function invalid(res) {
99
+ const { requestId } = meta(res);
100
+ return {
101
+ ok: false,
102
+ status: res.status,
103
+ error: "invalid_response",
104
+ ...requestId !== void 0 ? { requestId } : {}
105
+ };
106
+ }
107
+ async function call(schema, method, path, body, opts) {
108
+ const sent = await send(method, path, body, opts);
109
+ if (!("res" in sent)) return sent;
110
+ const { res } = sent;
111
+ if (!res.ok) return failure(res);
112
+ const json = await res.json().catch(() => void 0);
113
+ const parsed = schema.safeParse(json);
114
+ if (!parsed.success) return invalid(res);
115
+ return success(res, parsed.data);
116
+ }
117
+ async function empty(method, path, opts) {
118
+ const sent = await send(method, path, void 0, opts);
119
+ if (!("res" in sent)) return sent;
120
+ const { res } = sent;
121
+ if (!res.ok) return failure(res);
122
+ return success(res, null);
123
+ }
124
+ /** GET with `If-None-Match` (the previous response's `etag`): 304 → `{notModified: true}`. */
125
+ async function conditional(schema, path, etag) {
126
+ const sent = await send("GET", path, void 0, void 0, etag !== void 0 ? { "if-none-match": etag } : void 0);
127
+ if (!("res" in sent)) return sent;
128
+ const { res } = sent;
129
+ const responseEtag = res.headers.get("etag");
130
+ if (res.status === 304) {
131
+ const current = responseEtag ?? etag;
132
+ return current === void 0 ? invalid(res) : success(res, {
133
+ notModified: true,
134
+ etag: current
135
+ });
136
+ }
137
+ if (!res.ok) return failure(res);
138
+ const parsed = schema.safeParse(await res.json().catch(() => void 0));
139
+ if (!parsed.success) return invalid(res);
140
+ return success(res, {
141
+ notModified: false,
142
+ etag: responseEtag,
143
+ body: parsed.data
144
+ });
145
+ }
146
+ const ref = (value) => encodeURIComponent(value);
147
+ return {
148
+ getCatalog: () => call(CatalogSchema, "GET", "/v1/catalog"),
149
+ syncCatalog: (input, opts) => call(CatalogSyncResultSchema, "PUT", "/v1/admin/catalog", input, opts),
150
+ upsertAccount: (accountRef, input, opts) => call(AccountUpsertResponseSchema, "PUT", `/v1/accounts/${ref(accountRef)}`, input, opts),
151
+ getAccount: (accountRef, include = []) => call(AccountDetailSchema, "GET", `/v1/accounts/${ref(accountRef)}${include.length ? `?include=${include.join(",")}` : ""}`),
152
+ /**
153
+ * Hesap durumu (`AccountState`). ETag = `"<version>"`; önceki yanıtın
154
+ * `etag`'i verilirse ve durum değişmediyse 304 → `{notModified: true}`.
155
+ */
156
+ getAccountState: async (accountRef, opts = {}) => {
157
+ const result = await conditional(AccountStateSchema, `/v1/accounts/${ref(accountRef)}/state`, opts.etag);
158
+ if (!result.ok) return result;
159
+ const read = result.data;
160
+ return {
161
+ ...result,
162
+ data: read.notModified ? read : {
163
+ notModified: false,
164
+ etag: read.etag,
165
+ state: read.body
166
+ }
167
+ };
168
+ },
169
+ /** Atomic plan (K6): active plan grants → one plan grant (`planCode: null` removes it); `mode` is accepted and ignored (M35: no rank), `warnings` is always empty. */
170
+ setPlan: (accountRef, input, opts) => call(PlanSetResponseSchema, "PUT", `/v1/accounts/${ref(accountRef)}/plan`, input, opts),
171
+ /** Saklı durumu `account.state_changed` (`cause: "resync"`) olarak yeniden yayınlar; version değişmez (202). */
172
+ resync: (accountRef, opts) => call(ResyncResultSchema, "POST", `/v1/accounts/${ref(accountRef)}/resync`, void 0, opts),
173
+ /** KVKK anonimleştirme (F4e): `{ref, deletedAt}`; silinmişse 410 `account_deleted`, canlı abonelikte 409 `subscription_active`. */
174
+ deleteAccount: (accountRef, opts) => call(AccountDeleteResponseSchema, "DELETE", `/v1/accounts/${ref(accountRef)}`, void 0, opts),
175
+ createCheckoutSession: (accountRef, input, opts) => call(CheckoutSessionCreatedSchema, "POST", `/v1/accounts/${ref(accountRef)}/checkout-sessions`, input, opts),
176
+ getCheckoutSession: (id) => call(CheckoutSessionResponseSchema, "GET", `/v1/checkout-sessions/${ref(id)}`),
177
+ createPortalSession: (accountRef, input = {}, opts) => call(PortalSessionSchema, "POST", `/v1/accounts/${ref(accountRef)}/portal-sessions`, input, opts),
178
+ cancelSubscription: (id, input, opts) => call(CancelSubscriptionResponseSchema, "POST", `/v1/subscriptions/${ref(id)}/cancel`, input, opts),
179
+ /**
180
+ * Billing core (MF9): schedules a plan/interval/price change for the next period (errors:
181
+ * `SubscriptionChangeInputSchema`: 409 `already_subscribed`, 422 `plan_not_found`,
182
+ * `plan_not_sellable`, `subscription_change_invalid`).
183
+ */
184
+ changeSubscription: (id, input, opts) => call(ChangeSubscriptionResponseSchema, "POST", `/v1/subscriptions/${ref(id)}/change`, input, opts),
185
+ /**
186
+ * Billing core (MF9): takes back a cancellation scheduled for the period end; no body. 409
187
+ * `cancel_not_scheduled` (none scheduled) or `already_canceled` (ended, or its period is over).
188
+ */
189
+ uncancelSubscription: (id, opts) => call(UncancelSubscriptionResponseSchema, "POST", `/v1/subscriptions/${ref(id)}/uncancel`, void 0, opts),
190
+ createGrant: (accountRef, input, opts) => call(GrantCreateResponseSchema, "POST", `/v1/accounts/${ref(accountRef)}/grants`, input, opts),
191
+ revokeGrant: (id, opts) => empty("DELETE", `/v1/grants/${ref(id)}`, opts),
192
+ getStats: () => call(StatsSchema, "GET", "/v1/stats"),
193
+ doctor: () => call(DoctorReportSchema, "GET", "/v1/doctor"),
194
+ issueInvoice: (id, input, opts) => call(InvoiceIssueResponseSchema, "POST", `/v1/invoices/${ref(id)}/issue`, input, opts),
195
+ /** Manuel taslak fatura (F4f): profil gerekir (422 `billing_profile_required`); `issueInvoice` ile kesilir. */
196
+ createManualInvoice: (accountRef, input, opts) => call(ManualInvoiceResponseSchema, "POST", `/v1/accounts/${ref(accountRef)}/invoices`, input, opts),
197
+ /** Yalnızca manuel fatura (409 `invoice_has_payment` / `invoice_not_voidable`); `invoice.voided`. */
198
+ voidInvoice: (id, input, opts) => call(InvoiceVoidResponseSchema, "POST", `/v1/invoices/${ref(id)}/void`, input, opts),
199
+ /**
200
+ * Billing core: opens steward's hosted pay page for an unpaid invoice → `{id, url, expiresAt}`;
201
+ * redirect the customer to `url` (409 `invoice_not_payable`). Opening changes no state.
202
+ */
203
+ createInvoicePaymentSession: (id, input = {}, opts) => call(InvoicePaymentSessionSchema, "POST", `/v1/invoices/${ref(id)}/payment-sessions`, input, opts),
204
+ /** Billing core: forgives an unpaid invoice (409 `invoice_not_waivable`). */
205
+ waiveInvoice: (id, input, opts) => call(InvoiceWaiveResponseSchema, "POST", `/v1/invoices/${ref(id)}/waive`, input, opts),
206
+ /**
207
+ * `POST /v1/events/ingest`: 1–1 000 events → `{inserted, duplicates, rejected[]}`. Pass the SAME
208
+ * `idempotencyKey` on every retry of a batch (a new batch composition needs a new key);
209
+ * `externalId` dedups each event for good. 422 `batch_too_large`; 503 `events_backlog` (retry after `retryAfterMs`).
210
+ */
211
+ ingestEvents: (input, opts) => call(EventsIngestResultSchema, "POST", "/v1/events/ingest", input, opts),
212
+ /**
213
+ * `GET /v1/accounts/:ref/meters` (open period per meter). ETag = the account's meter sequence;
214
+ * with the previous `etag` an unchanged read is 304 → `{notModified: true}`.
215
+ */
216
+ getAccountMeters: async (accountRef, opts = {}) => {
217
+ const result = await conditional(AccountMetersSchema, `/v1/accounts/${ref(accountRef)}/meters`, opts.etag);
218
+ if (!result.ok) return result;
219
+ const read = result.data;
220
+ return {
221
+ ...result,
222
+ data: read.notModified ? read : {
223
+ notModified: false,
224
+ etag: read.etag,
225
+ meters: read.body
226
+ }
227
+ };
228
+ },
229
+ /** `GET /v1/accounts/:ref/meters/periods?limit=`: closed meter periods, newest first. */
230
+ listMeterPeriods: (accountRef, opts = {}) => call(MeterPeriodListSchema, "GET", `/v1/accounts/${ref(accountRef)}/meters/periods${opts.limit !== void 0 ? `?limit=${encodeURIComponent(String(opts.limit))}` : ""}`),
231
+ /** `POST /v1/accounts/:ref/credits`: one-off credit for a meter's open period (422 `meter_not_found`). */
232
+ grantCredits: (accountRef, input, opts) => call(CreditGrantResponseSchema, "POST", `/v1/accounts/${ref(accountRef)}/credits`, input, opts),
233
+ /** `GET /v1/accounts/:ref/meter-charges`: overage pricing records (also `dry_run` ones), newest first. */
234
+ listMeterCharges: (accountRef) => call(MeterChargeListSchema, "GET", `/v1/accounts/${ref(accountRef)}/meter-charges`),
235
+ getInvoiceDocument: async (id) => {
236
+ const sent = await send("GET", `/v1/invoices/${ref(id)}/document`, void 0, void 0);
237
+ if (!("res" in sent)) return sent;
238
+ const { res } = sent;
239
+ if (!res.ok) return failure(res);
240
+ return success(res, {
241
+ contentType: res.headers.get("content-type") ?? "application/octet-stream",
242
+ bytes: new Uint8Array(await res.arrayBuffer())
243
+ });
244
+ },
245
+ getSettings: () => call(ProductSettingsSchema, "GET", "/v1/admin/settings"),
246
+ updateSettings: (patch, opts) => call(ProductSettingsSchema, "PATCH", "/v1/admin/settings", patch, opts),
247
+ listWebhookEndpoints: () => call(WebhookEndpointListSchema, "GET", "/v1/admin/webhook-endpoints"),
248
+ createWebhookEndpoint: (input, opts) => call(WebhookEndpointSchema, "POST", "/v1/admin/webhook-endpoints", input, opts),
249
+ rotateWebhookEndpointSecret: (id, input, opts) => call(WebhookEndpointSchema, "POST", `/v1/admin/webhook-endpoints/${ref(id)}/rotate`, input, opts),
250
+ deactivateWebhookEndpoint: (id, opts) => call(WebhookEndpointSchema, "POST", `/v1/admin/webhook-endpoints/${ref(id)}/deactivate`, void 0, opts)
251
+ };
252
+ }
253
+ //#endregion
254
+ //#region src/retry.ts
255
+ const RETRY_CLASSES = [
256
+ "network",
257
+ "5xx",
258
+ "429"
259
+ ];
260
+ const BASE_DELAY_MS = 250;
261
+ function retryPolicy(options = {}) {
262
+ const attempts = options.attempts ?? 2;
263
+ const maxDelayMs = options.maxDelayMs ?? 5e3;
264
+ const on = options.on ?? RETRY_CLASSES;
265
+ const problems = [];
266
+ if (!Number.isInteger(attempts) || attempts < 0) problems.push(`retries.attempts negatif olmayan tam sayı olmalı: ${attempts}`);
267
+ if (!Number.isFinite(maxDelayMs) || maxDelayMs < 0) problems.push(`retries.maxDelayMs negatif olmayan sayı olmalı: ${maxDelayMs}`);
268
+ for (const value of on) if (!RETRY_CLASSES.includes(value)) problems.push(`retries.on bilinmeyen sınıf: ${String(value)} (${RETRY_CLASSES.join(", ")})`);
269
+ if (problems.length > 0) throw new StewardConfigError({ message: `createSteward: ${problems.join("; ")}` });
270
+ return {
271
+ attempts,
272
+ on: new Set(on),
273
+ maxDelayMs,
274
+ sleep: options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
275
+ random: options.random ?? Math.random
276
+ };
277
+ }
278
+ /** Başarısız sonucun tekrar sınıfı; hiçbirine girmiyorsa null (4xx, sözleşme hatası). */
279
+ function retryClassOf(failure) {
280
+ if (failure.status === 0) return "network";
281
+ if (failure.status === 429) return "429";
282
+ if (failure.status >= 500) return "5xx";
283
+ return null;
284
+ }
285
+ function shouldRetry(request, failure, policy) {
286
+ if (request.method !== "GET" && request.idempotencyKey === void 0) return false;
287
+ const kind = retryClassOf(failure);
288
+ if (kind === null || !policy.on.has(kind)) return false;
289
+ if (kind === "network") return true;
290
+ return (failure.retryable ?? errorCodeSpec(failure.error)?.retryable) !== false;
291
+ }
292
+ /** `retry`. tekrardan (1'den başlar) önceki bekleme, ms. */
293
+ function retryDelayMs(retry, failure, policy) {
294
+ if (failure.retryAfterMs !== void 0) return Math.min(failure.retryAfterMs, policy.maxDelayMs);
295
+ const ceiling = Math.min(policy.maxDelayMs, BASE_DELAY_MS * 2 ** (retry - 1));
296
+ return Math.round(ceiling / 2 + policy.random() * ceiling / 2);
297
+ }
298
+ /** `send`'i politika izin verdikçe tekrarlar; son sonucu (başarılı ya da değil) döner. */
299
+ async function withRetries(request, policy, send) {
300
+ let result = await send();
301
+ for (let retry = 1; retry <= policy.attempts && !result.ok && shouldRetry(request, result, policy); retry++) {
302
+ await policy.sleep(retryDelayMs(retry, result, policy));
303
+ result = await send();
304
+ }
305
+ return result;
306
+ }
307
+ //#endregion
308
+ //#region src/steward.ts
309
+ const SDK_HEADER = `@stewardhq/sdk/0.6.0`;
310
+ const defaultEnv = () => globalThis.process?.env ?? {};
311
+ function isHttpUrl(value) {
312
+ try {
313
+ const { protocol } = new URL(value);
314
+ return protocol === "http:" || protocol === "https:";
315
+ } catch {
316
+ return false;
317
+ }
318
+ }
319
+ /** Nesne ağacındaki her metodu, istek göndermeden `error(yol)` ile reddeden eşiyle değiştirir. */
320
+ function rejectAll(api, error, prefix = "") {
321
+ return Object.fromEntries(Object.entries(api).map(([name, value]) => {
322
+ const path = `${prefix}${name}`;
323
+ if (typeof value === "function") return [name, () => Promise.reject(error(path))];
324
+ return [name, typeof value === "object" && value !== null ? rejectAll(value, error, `${path}.`) : value];
325
+ }));
326
+ }
327
+ function createSteward(options = {}) {
328
+ const env = options.env ?? defaultEnv();
329
+ const setting = (value, name) => {
330
+ const text = (value ?? env[name])?.trim();
331
+ return text === void 0 || text === "" ? void 0 : text;
332
+ };
333
+ const baseUrl = setting(options.baseUrl, "STEWARD_URL");
334
+ const apiKey = setting(options.apiKey, "STEWARD_API_KEY");
335
+ const adminBaseUrl = setting(options.adminBaseUrl, "STEWARD_ADMIN_URL");
336
+ const adminApiKey = setting(options.adminApiKey, "STEWARD_ADMIN_API_KEY");
337
+ const timeoutMs = options.timeoutMs ?? 1e4;
338
+ const adminOnly = baseUrl === void 0 && apiKey === void 0 && adminBaseUrl !== void 0 && adminApiKey !== void 0;
339
+ const problems = [];
340
+ if (!adminOnly) {
341
+ if (baseUrl === void 0) problems.push("STEWARD_URL (baseUrl) tanımlı değil");
342
+ else if (!isHttpUrl(baseUrl)) problems.push("STEWARD_URL (baseUrl) geçerli bir http(s) adresi değil");
343
+ if (apiKey === void 0) problems.push("STEWARD_API_KEY (apiKey) tanımlı değil");
344
+ }
345
+ if (adminBaseUrl !== void 0 && !isHttpUrl(adminBaseUrl)) problems.push("STEWARD_ADMIN_URL (adminBaseUrl) geçerli bir http(s) adresi değil");
346
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) problems.push(`timeoutMs pozitif sayı olmalı: ${timeoutMs}`);
347
+ if (options.actorRef !== void 0 && !ActorRefSchema.safeParse(options.actorRef).success) problems.push("actorRef biçimi geçersiz (tür[:kimlik], ör. system, user:42)");
348
+ if (problems.length > 0) {
349
+ const hint = baseUrl === void 0 && apiKey === void 0 ? " (yalnızca admin.* için STEWARD_ADMIN_URL + STEWARD_ADMIN_API_KEY yeter)" : "";
350
+ throw new StewardConfigError({ message: `createSteward: ${problems.join("; ")}${hint}` });
351
+ }
352
+ const policy = retryPolicy(options.retries);
353
+ const doFetch = options.fetch ?? fetch;
354
+ const clientOptions = (target) => ({
355
+ ...target,
356
+ timeoutMs,
357
+ sdk: SDK_HEADER,
358
+ ...options.fetch !== void 0 ? { fetch: options.fetch } : {}
359
+ });
360
+ const adminTarget = adminBaseUrl !== void 0 && adminApiKey !== void 0 ? {
361
+ baseUrl: adminBaseUrl.replace(/\/$/, ""),
362
+ apiKey: adminApiKey
363
+ } : null;
364
+ const adminClient = adminTarget === null ? null : createBillingClient(clientOptions(adminTarget));
365
+ const productTarget = baseUrl !== void 0 && apiKey !== void 0 ? {
366
+ baseUrl: baseUrl.replace(/\/$/, ""),
367
+ apiKey
368
+ } : null;
369
+ const missingProduct = (name) => new StewardConfigError({ message: `steward.${name}: STEWARD_URL (baseUrl) ve STEWARD_API_KEY (apiKey) tanımlı değil; istemci yalnızca admin.* için yapılandırıldı` });
370
+ const raw = createBillingClient(clientOptions(productTarget ?? {
371
+ baseUrl: "http://steward.invalid",
372
+ apiKey: "-"
373
+ }));
374
+ const eitherTarget = productTarget ?? adminTarget;
375
+ const eitherClient = productTarget !== null ? raw : adminClient;
376
+ function requireAdmin() {
377
+ if (adminClient !== null && adminTarget !== null) return {
378
+ client: adminClient,
379
+ target: adminTarget
380
+ };
381
+ const missing = [...adminBaseUrl === void 0 ? ["STEWARD_ADMIN_URL (adminBaseUrl)"] : [], ...adminApiKey === void 0 ? ["STEWARD_ADMIN_API_KEY (adminApiKey)"] : []];
382
+ throw new StewardConfigError({ message: `steward.admin: ${missing.join(" ve ")} tanımlı değil` });
383
+ }
384
+ async function send(target, method, path, call, extraHeaders = {}) {
385
+ const headers = {
386
+ authorization: `Bearer ${target.apiKey}`,
387
+ accept: "application/json",
388
+ [HEADER_API_VERSION]: API_VERSION,
389
+ [HEADER_SDK]: SDK_HEADER,
390
+ ...extraHeaders
391
+ };
392
+ if (call.idempotencyKey !== void 0) headers[HEADER_IDEMPOTENCY_KEY] = call.idempotencyKey;
393
+ if (call.actorRef !== void 0) headers[HEADER_ACTOR_REF] = call.actorRef;
394
+ try {
395
+ return { res: await doFetch(`${target.baseUrl}${path}`, {
396
+ method,
397
+ headers,
398
+ signal: AbortSignal.timeout(timeoutMs)
399
+ }) };
400
+ } catch (error) {
401
+ const name = typeof error === "object" && error !== null ? error.name : void 0;
402
+ return {
403
+ ok: false,
404
+ status: 0,
405
+ error: name === "TimeoutError" || name === "AbortError" ? "timeout" : "network_error"
406
+ };
407
+ }
408
+ }
409
+ function meta(res) {
410
+ const requestId = res.headers.get(HEADER_REQUEST_ID);
411
+ const retryAfterMs = res.status === 429 || res.status === 503 ? parseRetryAfter(res.headers.get("retry-after")) : null;
412
+ return {
413
+ ...requestId ? { requestId } : {},
414
+ ...retryAfterMs !== null ? { retryAfterMs } : {}
415
+ };
416
+ }
417
+ async function failureOf(res) {
418
+ const parsed = ErrorResponseSchema.safeParse(await res.json().catch(() => null));
419
+ const { requestId, retryAfterMs } = meta(res);
420
+ const common = { ...retryAfterMs !== void 0 ? { retryAfterMs } : {} };
421
+ if (!parsed.success) return {
422
+ ok: false,
423
+ status: res.status,
424
+ error: `http_${res.status}`,
425
+ ...requestId !== void 0 ? { requestId } : {},
426
+ ...common
427
+ };
428
+ const { error, message, category, retryable } = parsed.data;
429
+ const id = requestId ?? parsed.data.requestId;
430
+ return {
431
+ ok: false,
432
+ status: res.status,
433
+ error,
434
+ ...message !== void 0 ? { message } : {},
435
+ ...category !== void 0 ? { category } : {},
436
+ ...retryable !== void 0 ? { retryable } : {},
437
+ ...id !== void 0 ? { requestId: id } : {},
438
+ ...common
439
+ };
440
+ }
441
+ function success(res, data) {
442
+ const { requestId } = meta(res);
443
+ return {
444
+ ok: true,
445
+ status: res.status,
446
+ data,
447
+ ...requestId !== void 0 ? { requestId } : {}
448
+ };
449
+ }
450
+ function invalid(res) {
451
+ const { requestId } = meta(res);
452
+ return {
453
+ ok: false,
454
+ status: res.status,
455
+ error: "invalid_response",
456
+ ...requestId !== void 0 ? { requestId } : {}
457
+ };
458
+ }
459
+ async function sendParsed(target, method, path, schema, call) {
460
+ const sent = await send(target, method, path, call);
461
+ if (!("res" in sent)) return sent;
462
+ if (!sent.res.ok) return failureOf(sent.res);
463
+ const parsed = schema.safeParse(await sent.res.json().catch(() => void 0));
464
+ return parsed.success ? success(sent.res, parsed.data) : invalid(sent.res);
465
+ }
466
+ async function readCatalog(etag) {
467
+ const sent = await send(productTarget, "GET", "/v1/catalog", {}, etag !== void 0 ? { "if-none-match": etag } : {});
468
+ if (!("res" in sent)) return sent;
469
+ const { res } = sent;
470
+ const responseEtag = res.headers.get("etag");
471
+ if (res.status === 304) {
472
+ const current = responseEtag ?? etag;
473
+ return current === void 0 ? invalid(res) : success(res, {
474
+ notModified: true,
475
+ etag: current
476
+ });
477
+ }
478
+ if (!res.ok) return failureOf(res);
479
+ const parsed = CatalogSchema.safeParse(await res.json().catch(() => void 0));
480
+ return parsed.success ? success(res, {
481
+ notModified: false,
482
+ etag: responseEtag,
483
+ catalog: parsed.data
484
+ }) : invalid(res);
485
+ }
486
+ function toError(failure) {
487
+ const context = {
488
+ ...failure.requestId !== void 0 ? { requestId: failure.requestId } : {},
489
+ ...failure.retryAfterMs !== void 0 ? { retryAfterMs: failure.retryAfterMs } : {}
490
+ };
491
+ if (failure.status === 0) return new StewardNetworkError({
492
+ code: failure.error === "timeout" ? "timeout" : "network_error",
493
+ ...context
494
+ });
495
+ if (failure.error === "invalid_response") return new StewardContractError({
496
+ httpStatus: failure.status,
497
+ ...context
498
+ });
499
+ return new StewardApiError({
500
+ code: failure.error,
501
+ httpStatus: failure.status,
502
+ ...failure.message !== void 0 ? { message: failure.message } : {},
503
+ ...failure.category !== void 0 ? { category: failure.category } : {},
504
+ ...failure.retryable !== void 0 ? { retryable: failure.retryable } : {},
505
+ ...context
506
+ });
507
+ }
508
+ async function run(method, call, attempt) {
509
+ let effective = {};
510
+ if (method !== "GET") {
511
+ const actorRef = call?.actorRef ?? options.actorRef;
512
+ if (actorRef === void 0) throw new StewardConfigError({
513
+ code: "actor_ref_required",
514
+ message: "actorRef yok: createSteward({ actorRef }) ya da çağrıda { actorRef } verin (tür[:kimlik], ör. user:42, system)"
515
+ });
516
+ if (!ActorRefSchema.safeParse(actorRef).success) throw new StewardConfigError({
517
+ code: "invalid_actor_ref",
518
+ message: "actorRef biçimi geçersiz (tür[:kimlik], ör. user:42, system)"
519
+ });
520
+ effective = {
521
+ idempotencyKey: call?.idempotencyKey ?? globalThis.crypto.randomUUID(),
522
+ actorRef
523
+ };
524
+ }
525
+ const result = await withRetries({
526
+ method,
527
+ ...effective.idempotencyKey !== void 0 ? { idempotencyKey: effective.idempotencyKey } : {}
528
+ }, policy, () => attempt(effective));
529
+ if (result.ok) return result.data;
530
+ throw toError(result);
531
+ }
532
+ const read = (attempt) => run("GET", void 0, attempt);
533
+ const productApi = {
534
+ accounts: {
535
+ upsert: (ref, input, call) => run("PUT", call, (c) => raw.upsertAccount(ref, input, c)),
536
+ get: (ref, opts = {}) => read(() => raw.getAccount(ref, opts.include)),
537
+ state: async (ref) => {
538
+ const result = await read(() => raw.getAccountState(ref));
539
+ if (result.notModified) throw new StewardContractError({ httpStatus: 304 });
540
+ return result.state;
541
+ },
542
+ readState: (ref, opts = {}) => read(() => raw.getAccountState(ref, opts.etag !== void 0 ? { etag: opts.etag } : {})),
543
+ setPlan: (ref, input, call) => run("PUT", call, (c) => raw.setPlan(ref, input, c)),
544
+ resync: (ref, call) => run("POST", call, (c) => raw.resync(ref, c)),
545
+ delete: (ref, call) => run("DELETE", call, (c) => raw.deleteAccount(ref, c))
546
+ },
547
+ checkoutSessions: {
548
+ create: (ref, input, call) => run("POST", call, (c) => raw.createCheckoutSession(ref, input, c)),
549
+ get: (id) => read(() => raw.getCheckoutSession(id))
550
+ },
551
+ portalSessions: { create: (ref, input = {}, call) => run("POST", call, (c) => raw.createPortalSession(ref, input, c)) },
552
+ subscriptions: {
553
+ cancel: (id, input, call) => run("POST", call, (c) => raw.cancelSubscription(id, input, c)),
554
+ change: (id, input, call) => run("POST", call, (c) => raw.changeSubscription(id, input, c)),
555
+ uncancel: (id, call) => run("POST", call, (c) => raw.uncancelSubscription(id, c))
556
+ },
557
+ grants: {
558
+ create: (ref, input, call) => run("POST", call, (c) => raw.createGrant(ref, input, c)),
559
+ revoke: async (id, call) => {
560
+ await run("DELETE", call, (c) => raw.revokeGrant(id, c));
561
+ }
562
+ },
563
+ invoices: {
564
+ issue: (id, input, call) => run("POST", call, (c) => raw.issueInvoice(id, input, c)),
565
+ document: (id) => read(() => raw.getInvoiceDocument(id)),
566
+ createManual: (ref, input, call) => run("POST", call, (c) => raw.createManualInvoice(ref, input, c)),
567
+ void: (id, input, call) => run("POST", call, (c) => raw.voidInvoice(id, input, c)),
568
+ createPaymentSession: (id, input = {}, call) => run("POST", call, (c) => raw.createInvoicePaymentSession(id, input, c)),
569
+ waive: (id, input, call) => run("POST", call, (c) => raw.waiveInvoice(id, input, c))
570
+ },
571
+ catalog: {
572
+ get: () => read(() => raw.getCatalog()),
573
+ read: (opts = {}) => read(() => readCatalog(opts.etag))
574
+ },
575
+ events: { ingest: (input, call) => run("POST", call, (c) => raw.ingestEvents(input, c)) },
576
+ meters: {
577
+ get: async (ref) => {
578
+ const result = await read(() => raw.getAccountMeters(ref));
579
+ if (result.notModified) throw new StewardContractError({ httpStatus: 304 });
580
+ return result.meters;
581
+ },
582
+ read: (ref, opts = {}) => read(() => raw.getAccountMeters(ref, opts.etag !== void 0 ? { etag: opts.etag } : {})),
583
+ periods: (ref, opts = {}) => read(() => raw.listMeterPeriods(ref, opts.limit !== void 0 ? { limit: opts.limit } : {}))
584
+ },
585
+ credits: { grant: (ref, input, call) => run("POST", call, (c) => raw.grantCredits(ref, input, c)) },
586
+ meterCharges: { list: (ref) => read(() => raw.listMeterCharges(ref)) },
587
+ stats: () => read(() => raw.getStats())
588
+ };
589
+ return {
590
+ ...productTarget !== null ? productApi : rejectAll(productApi, missingProduct),
591
+ me: () => read(() => sendParsed(eitherTarget, "GET", "/v1/me", MeResponseSchema, {})),
592
+ doctor: () => read(() => eitherClient.doctor()),
593
+ admin: {
594
+ catalog: { sync: async (input, call) => {
595
+ const { client } = requireAdmin();
596
+ return run("PUT", call, (c) => client.syncCatalog(input, c));
597
+ } },
598
+ accounts: { refresh: async (call) => {
599
+ const { target } = requireAdmin();
600
+ return run("POST", call, (c) => sendParsed(target, "POST", "/v1/admin/accounts/refresh", AccountsRefreshResultSchema, c));
601
+ } },
602
+ settings: {
603
+ get: async () => {
604
+ const { client } = requireAdmin();
605
+ return read(() => client.getSettings());
606
+ },
607
+ update: async (patch, call) => {
608
+ const { client } = requireAdmin();
609
+ return run("PATCH", call, (c) => client.updateSettings(patch, c));
610
+ }
611
+ },
612
+ endpoints: {
613
+ list: async () => {
614
+ const { client } = requireAdmin();
615
+ return read(() => client.listWebhookEndpoints());
616
+ },
617
+ create: async (input, call) => {
618
+ const { client } = requireAdmin();
619
+ return run("POST", call, (c) => client.createWebhookEndpoint(input, c));
620
+ },
621
+ rotate: async (id, input, call) => {
622
+ const { client } = requireAdmin();
623
+ return run("POST", call, (c) => client.rotateWebhookEndpointSecret(id, input, c));
624
+ },
625
+ deactivate: async (id, call) => {
626
+ const { client } = requireAdmin();
627
+ return run("POST", call, (c) => client.deactivateWebhookEndpoint(id, c));
628
+ }
629
+ }
630
+ }
631
+ };
632
+ }
633
+ //#endregion
6
634
  //#region src/state-cache.ts
7
635
  const MANY_CONCURRENCY = 8;
8
- const FAILURE_BACKOFF_MS = 5e3;
9
- function fieldsOf$1(error) {
636
+ const FAILURE_BACKOFF_MS$1 = 5e3;
637
+ function fieldsOf$3(error) {
10
638
  const { code, httpStatus, requestId } = typeof error === "object" && error !== null ? error : {};
11
639
  return {
12
640
  code: typeof code === "string" ? code : null,
@@ -14,7 +642,7 @@ function fieldsOf$1(error) {
14
642
  requestId: typeof requestId === "string" ? requestId : null
15
643
  };
16
644
  }
17
- function isOutage({ code, httpStatus }) {
645
+ function isOutage$1({ code, httpStatus }) {
18
646
  if (httpStatus === 0) return code === "network_error" || code === "timeout";
19
647
  return httpStatus !== null && (httpStatus >= 500 || httpStatus === 429);
20
648
  }
@@ -34,7 +662,7 @@ function stateCache(steward, options = {}) {
34
662
  const { catalog, fallback, onFallback } = options;
35
663
  const logger = options.logger ?? console;
36
664
  const now = options.now ?? Date.now;
37
- const backoffMs = Math.min(ttlMs, FAILURE_BACKOFF_MS);
665
+ const backoffMs = Math.min(ttlMs, FAILURE_BACKOFF_MS$1);
38
666
  const entries = /* @__PURE__ */ new Map();
39
667
  const flights = /* @__PURE__ */ new Map();
40
668
  let seq = 0;
@@ -67,7 +695,7 @@ function stateCache(steward, options = {}) {
67
695
  /** Kesintide: bayat değer → fallback → asıl hata. WARN her başarısız istekte ve bayattan fallback'e geçişte bir kez. */
68
696
  async function degrade(ref, failure) {
69
697
  const { error } = failure;
70
- const { code, requestId } = fieldsOf$1(error);
698
+ const { code, requestId } = fieldsOf$3(error);
71
699
  const entry = entries.get(ref);
72
700
  const t = now();
73
701
  if (entry?.state && t < entry.fetchedAt + ttlMs + staleIfErrorMs) {
@@ -148,7 +776,7 @@ function stateCache(steward, options = {}) {
148
776
  try {
149
777
  read = await steward.accounts.readState(ref, known?.state && known.etag !== null ? { etag: known.etag } : {});
150
778
  } catch (error) {
151
- const fields = fieldsOf$1(error);
779
+ const fields = fieldsOf$3(error);
152
780
  if (isNotFound(fields)) {
153
781
  if (missing === "throw") {
154
782
  entries.delete(ref);
@@ -170,7 +798,7 @@ function stateCache(steward, options = {}) {
170
798
  });
171
799
  return missingFallback(ref, failure);
172
800
  }
173
- if (!isOutage(fields)) throw error;
801
+ if (!isOutage$1(fields)) throw error;
174
802
  const current = entries.get(ref);
175
803
  const failure = {
176
804
  error,
@@ -263,6 +891,10 @@ function stateCache(steward, options = {}) {
263
891
  if (current?.state && account.version < current.state.version) return;
264
892
  const etag = current?.state?.version === account.version ? current.etag : null;
265
893
  put(account.ref, confirmed(account, etag, now()));
894
+ },
895
+ peek(ref) {
896
+ const entry = entries.get(ref);
897
+ return entry?.state && now() < entry.fetchedAt + ttlMs + staleIfErrorMs ? entry.state : null;
266
898
  }
267
899
  };
268
900
  }
@@ -286,7 +918,7 @@ const BY_CATEGORY = {
286
918
  unavailable: 503
287
919
  };
288
920
  const isCategory = (value) => typeof value === "string" && ERROR_CATEGORIES.includes(value);
289
- function fieldsOf(err) {
921
+ function fieldsOf$2(err) {
290
922
  if (typeof err !== "object" || err === null) return null;
291
923
  const { code, category, retryable, requestId, retryAfterMs } = err;
292
924
  if (typeof code !== "string") return null;
@@ -304,7 +936,7 @@ function fieldsOf(err) {
304
936
  const retryAfter = (fields) => fields.retryAfterMs === void 0 ? {} : { "Retry-After": String(Math.max(1, Math.ceil(fields.retryAfterMs / 1e3))) };
305
937
  function httpStatusFor(err, options = {}) {
306
938
  const logger = options.logger ?? console;
307
- const fields = fieldsOf(err);
939
+ const fields = fieldsOf$2(err);
308
940
  if (!fields) return {
309
941
  status: 500,
310
942
  headers: {}
@@ -356,6 +988,46 @@ function httpStatusFor(err, options = {}) {
356
988
  };
357
989
  }
358
990
  //#endregion
991
+ //#region src/meter-catalog.ts
992
+ function meterCatalogFacts(input) {
993
+ const meters = new Map(Object.entries(input.meters));
994
+ const credits = /* @__PURE__ */ new Map();
995
+ const creditsOf = (plan) => {
996
+ const known = credits.get(plan);
997
+ if (known !== void 0) return known;
998
+ const { benefits } = resolveBenefits({
999
+ catalog: input,
1000
+ subscriptions: plan === input.defaultPlanCode ? [] : [{
1001
+ planCode: plan,
1002
+ status: "active"
1003
+ }]
1004
+ });
1005
+ const granted = meterCreditsOf(benefits, { defaultPlanCode: input.defaultPlanCode });
1006
+ const out = new Map([...granted].map(([meter, credit]) => [meter, credit.unitsMicro]));
1007
+ credits.set(plan, out);
1008
+ return out;
1009
+ };
1010
+ const billableOf = (plan, meter) => plan !== input.defaultPlanCode && input.meteredPrices.some((price) => price.planCode === plan && price.meter === meter);
1011
+ const stateBlockOf = (plan) => {
1012
+ const planCredits = creditsOf(plan);
1013
+ return Object.fromEntries([...meters.keys()].map((code) => {
1014
+ const credit = planCredits.get(code);
1015
+ return [code, {
1016
+ includedUnits: credit === void 0 ? null : fromMicroUnits(credit),
1017
+ billable: billableOf(plan, code),
1018
+ standing: "ok"
1019
+ }];
1020
+ }));
1021
+ };
1022
+ return {
1023
+ defaultPlan: input.defaultPlanCode,
1024
+ meters,
1025
+ creditsOf,
1026
+ billableOf,
1027
+ stateBlockOf
1028
+ };
1029
+ }
1030
+ //#endregion
359
1031
  //#region src/catalog.ts
360
1032
  const feature = (type, options) => options?.description === void 0 ? { type } : {
361
1033
  type,
@@ -367,6 +1039,75 @@ const limit = (options) => feature("limit", options);
367
1039
  const flag = (options) => feature("boolean", options);
368
1040
  /** Serbest metin ayarı. */
369
1041
  const text = (options) => feature("string", options);
1042
+ /**
1043
+ * A `feature_flag` benefit: feature key → value it grants, e.g. `featureFlag({ sso: true })`.
1044
+ * Keys are catalog features and values follow the feature's type (checked by `defineCatalog`).
1045
+ */
1046
+ function featureFlag(properties, options) {
1047
+ const copy = { ...properties };
1048
+ const description = options?.description;
1049
+ return description === void 0 ? {
1050
+ type: "feature_flag",
1051
+ properties: copy
1052
+ } : {
1053
+ type: "feature_flag",
1054
+ properties: copy,
1055
+ description: { ...description }
1056
+ };
1057
+ }
1058
+ /**
1059
+ * A `meter_credit` benefit (metering phase 2, M5): `meterCredit("ai_tokens", { units: 50_000 })`,
1060
+ * optionally `rollover: { capUnits }`. The meter must be a catalog meter (checked by `defineCatalog`);
1061
+ * units may be decimals with at most 6 places.
1062
+ */
1063
+ function meterCredit(meter, options) {
1064
+ const given = isRecord(options) ? options : {};
1065
+ const rollover = given.rollover;
1066
+ const properties = {
1067
+ meter,
1068
+ units: given.units,
1069
+ ...rollover !== void 0 ? { rolloverCapUnits: isRecord(rollover) ? rollover.capUnits : null } : {}
1070
+ };
1071
+ const description = given.description;
1072
+ return description === void 0 ? {
1073
+ type: "meter_credit",
1074
+ properties
1075
+ } : {
1076
+ type: "meter_credit",
1077
+ properties,
1078
+ description: copyOf(description)
1079
+ };
1080
+ }
1081
+ /**
1082
+ * Aggregations (M4): `count()` events, `sum(key)` / `max(key)` of the finite, non-negative number
1083
+ * at `metadata.<key>` (decimals allowed), `unique(key)` distinct values at `metadata.<key>`.
1084
+ */
1085
+ const count = () => ({ kind: "count" });
1086
+ const sum = (key) => ({
1087
+ kind: "sum",
1088
+ key
1089
+ });
1090
+ const max = (key) => ({
1091
+ kind: "max",
1092
+ key
1093
+ });
1094
+ const unique = (key) => ({
1095
+ kind: "unique",
1096
+ key
1097
+ });
1098
+ function meter(options) {
1099
+ const given = isRecord(options) ? options : {};
1100
+ const spec = {
1101
+ aggregation: copyDeep(given.aggregate),
1102
+ label: copyDeep(given.label)
1103
+ };
1104
+ if (given.event !== void 0) spec.event = given.event;
1105
+ if (given.filter !== void 0) spec.filter = copyDeep(given.filter);
1106
+ if (given.unitLabel !== void 0) spec.unitLabel = copyDeep(given.unitLabel);
1107
+ if (given.displayPer !== void 0) spec.displayPer = given.displayPer;
1108
+ if (given.lowAt !== void 0) spec.lowAt = given.lowAt;
1109
+ return spec;
1110
+ }
370
1111
  function matches(type, value) {
371
1112
  switch (type) {
372
1113
  case "limit": return value === null || typeof value === "number" && Number.isInteger(value) && value >= 0;
@@ -379,9 +1120,35 @@ const EXPECTED = {
379
1120
  boolean: "boolean",
380
1121
  string: "string"
381
1122
  };
1123
+ const EXPECTED_EN = {
1124
+ limit: "non-negative integer or null",
1125
+ boolean: "boolean",
1126
+ string: "string"
1127
+ };
382
1128
  const show = (value) => value === void 0 ? "yok" : JSON.stringify(value);
1129
+ const showValue = (value) => value === void 0 ? "nothing" : JSON.stringify(value);
383
1130
  const has = (object, key) => Object.hasOwn(object, key);
384
1131
  const isObject = (value) => typeof value === "object" && value !== null;
1132
+ const isRecord = (value) => isObject(value) && !Array.isArray(value);
1133
+ const copyOf = (value) => Array.isArray(value) ? [...value] : isObject(value) ? { ...value } : value;
1134
+ /** Deep copy of plain JSON-like data (arrays and objects); other values as they are. */
1135
+ const copyDeep = (value) => Array.isArray(value) ? value.map(copyDeep) : isObject(value) ? Object.fromEntries(Object.entries(value).map(([k, v]) => [k, copyDeep(v)])) : value;
1136
+ /** Deep freeze of data made by `copyDeep`. */
1137
+ const freezeDeep = (value) => {
1138
+ if (isObject(value)) {
1139
+ for (const inner of Object.values(value)) freezeDeep(inner);
1140
+ Object.freeze(value);
1141
+ }
1142
+ return value;
1143
+ };
1144
+ const benefitCopy = ({ type, properties, description }) => description === void 0 ? {
1145
+ type,
1146
+ properties: { ...properties }
1147
+ } : {
1148
+ type,
1149
+ properties: { ...properties },
1150
+ description: copyOf(description)
1151
+ };
385
1152
  function invalid(problems) {
386
1153
  return new StewardConfigError({
387
1154
  code: "invalid_catalog",
@@ -402,7 +1169,39 @@ function defineCatalog(definition) {
402
1169
  if (!isObject(input.features)) problems.push("features: nesne bekleniyor");
403
1170
  if (!isObject(input.plans)) problems.push("plans: nesne bekleniyor");
404
1171
  if (!Array.isArray(input.prices)) problems.push("prices: dizi bekleniyor");
1172
+ if (input.benefits !== void 0 && !isRecord(input.benefits)) problems.push("benefits: object expected");
1173
+ if (input.meters !== void 0 && !isRecord(input.meters)) problems.push("meters: object expected");
1174
+ if (input.meteredPrices !== void 0 && !Array.isArray(input.meteredPrices)) problems.push("meteredPrices: array expected");
405
1175
  if (problems.length > 0) throw invalid(problems);
1176
+ const meters = /* @__PURE__ */ new Map();
1177
+ for (const [code, spec] of Object.entries(input.meters ?? {})) {
1178
+ if (!isRecord(spec) || !isRecord(spec.aggregation) || typeof spec.aggregation.kind !== "string") {
1179
+ problems.push(`meters.${code}: meter({ aggregate: count() | sum(key) | max(key) | unique(key), … }) expected`);
1180
+ continue;
1181
+ }
1182
+ meters.set(code, copyDeep(spec));
1183
+ }
1184
+ const benefitCodes = new Set(Object.keys(input.benefits ?? {}));
1185
+ const benefits = /* @__PURE__ */ new Map();
1186
+ for (const [code, spec] of Object.entries(input.benefits ?? {})) {
1187
+ if (!isRecord(spec) || spec.type !== "feature_flag" && spec.type !== "meter_credit" || !isRecord(spec.properties)) {
1188
+ problems.push(`benefits.${code}: featureFlag() or meterCredit() expected`);
1189
+ continue;
1190
+ }
1191
+ if (spec.type === "meter_credit") {
1192
+ const meterCode = spec.properties.meter;
1193
+ if (typeof meterCode !== "string" || !meters.has(meterCode)) problems.push(`benefits.${code}.properties.meter: undefined meter ${showValue(meterCode)}`);
1194
+ } else for (const [key, value] of Object.entries(spec.properties)) {
1195
+ const feature = features.get(key);
1196
+ if (feature === void 0) problems.push(`benefits.${code}.properties.${key}: undefined feature`);
1197
+ else if (!matches(feature.type, value)) problems.push(`benefits.${code}.properties.${key}: ${EXPECTED_EN[feature.type]} expected, got ${showValue(value)}`);
1198
+ }
1199
+ benefits.set(code, benefitCopy({
1200
+ type: spec.type,
1201
+ properties: spec.properties,
1202
+ ...spec.description !== void 0 ? { description: spec.description } : {}
1203
+ }));
1204
+ }
406
1205
  const plans = /* @__PURE__ */ new Map();
407
1206
  for (const [code, plan] of Object.entries(input.plans)) {
408
1207
  const given = isObject(plan) && isObject(plan.entitlements) ? plan.entitlements : {};
@@ -413,13 +1212,25 @@ function defineCatalog(definition) {
413
1212
  entitlements[key] = given[key];
414
1213
  }
415
1214
  for (const key of Object.keys(given).filter((k) => !features.has(k))) problems.push(`plans.${code}.entitlements.${key}: tanımsız feature`);
416
- const { name, rank, sellable } = isObject(plan) ? plan : {};
417
- plans.set(code, {
1215
+ const { name, rank, sellable, benefits: listed } = isObject(plan) ? plan : {};
1216
+ const runtime = {
418
1217
  name,
419
- rank,
420
1218
  sellable,
421
1219
  entitlements
422
- });
1220
+ };
1221
+ if (rank !== void 0) runtime.rank = rank;
1222
+ if (listed !== void 0) {
1223
+ if (Array.isArray(listed)) {
1224
+ const seen = /* @__PURE__ */ new Set();
1225
+ for (const [i, benefit] of listed.entries()) {
1226
+ if (typeof benefit !== "string" || !benefitCodes.has(benefit)) problems.push(`plans.${code}.benefits.${i}: undefined benefit ${showValue(benefit)}`);
1227
+ else if (seen.has(benefit)) problems.push(`plans.${code}.benefits.${i}: duplicate benefit ${benefit}`);
1228
+ seen.add(benefit);
1229
+ }
1230
+ runtime.benefits = [...listed];
1231
+ } else problems.push(`plans.${code}.benefits: array of benefit codes expected`);
1232
+ }
1233
+ plans.set(code, runtime);
423
1234
  }
424
1235
  if (typeof input.defaultPlan !== "string" || !plans.has(input.defaultPlan)) problems.push(`defaultPlan: ${show(input.defaultPlan)} planlarda yok`);
425
1236
  const prices = input.prices.map((price, i) => {
@@ -427,6 +1238,12 @@ function defineCatalog(definition) {
427
1238
  if (typeof copy.plan !== "string" || !plans.has(copy.plan)) problems.push(`prices.${i}.plan: ${show(copy.plan)} planlarda yok`);
428
1239
  return copy;
429
1240
  });
1241
+ const meteredPrices = (input.meteredPrices ?? []).map((price, i) => {
1242
+ const copy = { ...isObject(price) ? price : {} };
1243
+ if (typeof copy.plan !== "string" || !plans.has(copy.plan)) problems.push(`meteredPrices.${i}.plan: undefined plan ${showValue(copy.plan)}`);
1244
+ if (typeof copy.meter !== "string" || !meters.has(copy.meter)) problems.push(`meteredPrices.${i}.meter: undefined meter ${showValue(copy.meter)}`);
1245
+ return copy;
1246
+ });
430
1247
  if (problems.length > 0) throw invalid(problems);
431
1248
  const toSyncInput = () => ({
432
1249
  defaultPlanCode: input.defaultPlan,
@@ -434,15 +1251,24 @@ function defineCatalog(definition) {
434
1251
  key,
435
1252
  ...spec
436
1253
  })),
437
- plans: [...plans].map(([code, plan]) => ({
1254
+ plans: [...plans].map(([code, { name, rank, sellable, entitlements, benefits: listed }]) => ({
438
1255
  code,
439
- ...plan,
440
- entitlements: { ...plan.entitlements }
1256
+ name,
1257
+ rank: rank === void 0 ? 0 : rank,
1258
+ sellable,
1259
+ entitlements: { ...entitlements },
1260
+ ...listed !== void 0 && listed.length > 0 ? { benefits: [...listed] } : {}
441
1261
  })),
442
1262
  prices: prices.map(({ plan, ...rest }) => ({
443
1263
  planCode: plan,
444
1264
  ...rest
445
- }))
1265
+ })),
1266
+ ...benefits.size > 0 ? { benefits: Object.fromEntries([...benefits].map(([code, benefit]) => [code, benefitCopy(benefit)])) } : {},
1267
+ ...meters.size > 0 ? { meters: Object.fromEntries([...meters].map(([code, spec]) => [code, copyDeep(spec)])) } : {},
1268
+ ...meteredPrices.length > 0 ? { meteredPrices: meteredPrices.map(({ plan, ...rest }) => ({
1269
+ planCode: plan,
1270
+ ...rest
1271
+ })) } : {}
446
1272
  });
447
1273
  const parsed = CatalogSyncInputSchema.safeParse(toSyncInput());
448
1274
  if (!parsed.success) {
@@ -461,7 +1287,14 @@ function defineCatalog(definition) {
461
1287
  }
462
1288
  const defaultPlan = input.defaultPlan;
463
1289
  const isPlan = (code) => typeof code === "string" && plans.has(code);
464
- const planEntitlements = (code) => plans.get(code).entitlements;
1290
+ const resolved = new Map([...plans.keys()].map((code) => [code, Object.freeze(resolveEntitlements({
1291
+ catalog: parsed.data,
1292
+ subscription: code === defaultPlan ? null : {
1293
+ planCode: code,
1294
+ status: "active"
1295
+ }
1296
+ }).entitlements)]));
1297
+ const planEntitlements = (code) => resolved.get(code);
465
1298
  const entitlementsOf = (plan) => {
466
1299
  if (!isPlan(plan)) throw new StewardConfigError({
467
1300
  code: "unknown_plan",
@@ -483,6 +1316,16 @@ function defineCatalog(definition) {
483
1316
  if (fellBack.length > 0) options.onFallback?.(fellBack);
484
1317
  return result;
485
1318
  };
1319
+ const facts = meters.size > 0 ? meterCatalogFacts(parsed.data) : null;
1320
+ const metersBlocks = /* @__PURE__ */ new Map();
1321
+ const metersBlockOf = (plan) => {
1322
+ let block = metersBlocks.get(plan);
1323
+ if (block === void 0) {
1324
+ block = facts.stateBlockOf(plan);
1325
+ metersBlocks.set(plan, block);
1326
+ }
1327
+ return Object.fromEntries(Object.entries(block).map(([code, meterState]) => [code, { ...meterState }]));
1328
+ };
486
1329
  const stateOf = (ref, plan = defaultPlan, options = {}) => ({
487
1330
  ref,
488
1331
  displayName: options.displayName ?? ref,
@@ -497,19 +1340,32 @@ function defineCatalog(definition) {
497
1340
  profile: {
498
1341
  present: false,
499
1342
  kind: null
500
- }
1343
+ },
1344
+ ...facts !== null ? { meters: metersBlockOf(plan) } : {}
501
1345
  });
502
1346
  const frozen = (value) => Object.freeze(value);
1347
+ const frozenBenefit = ({ properties, description, ...rest }) => frozen({
1348
+ ...rest,
1349
+ properties: frozen({ ...properties }),
1350
+ ...isObject(description) ? { description: frozen({ ...description }) } : {}
1351
+ });
503
1352
  return frozen({
504
1353
  defaultPlan,
505
1354
  features: frozen(Object.fromEntries([...features].map(([key, spec]) => [key, frozen({ ...spec })]))),
506
- plans: frozen(Object.fromEntries([...plans].map(([code, plan]) => [code, frozen({
1355
+ plans: frozen(Object.fromEntries([...plans].map(([code, { benefits: listed, ...plan }]) => [code, frozen({
507
1356
  ...plan,
508
- entitlements: frozen({ ...plan.entitlements })
1357
+ entitlements: frozen({ ...plan.entitlements }),
1358
+ ...listed !== void 0 ? { benefits: frozen([...listed]) } : {}
509
1359
  })]))),
510
1360
  prices: frozen(prices.map((price) => frozen({ ...price }))),
1361
+ benefits: frozen(Object.fromEntries([...benefits].map(([code, benefit]) => [code, frozenBenefit(benefit)]))),
1362
+ meters: frozen(Object.fromEntries([...meters].map(([code, spec]) => [code, freezeDeep(copyDeep(spec))]))),
1363
+ meteredPrices: frozen(meteredPrices.map((price) => frozen({ ...price }))),
511
1364
  $plan: void 0,
512
1365
  $entitlements: void 0,
1366
+ $benefit: void 0,
1367
+ $meter: void 0,
1368
+ $event: void 0,
513
1369
  toSyncInput,
514
1370
  entitlementsOf,
515
1371
  resolve,
@@ -518,4 +1374,781 @@ function defineCatalog(definition) {
518
1374
  });
519
1375
  }
520
1376
  //#endregion
521
- export { ACCOUNT_INCLUDES, API_VERSION, AccessStateSchema, AccountDeleteResponseSchema, AccountDetailSchema, AccountDunningStateSchema, AccountLocaleSchema, AccountOpenCheckoutSchema, AccountProfileStateSchema, AccountScheduledChangeSchema, AccountSchema, AccountStateSchema, AccountSubscriptionStateSchema, AccountUpsertInputSchema, AccountUpsertResponseSchema, AccountsRefreshResultSchema, ActorRefSchema, AmountMinorSchema, AnyBillingEventSchema, ApiVersionSchema, BILLING_PATH_PLACEHOLDERS, BillingEventSchema, BillingPathSchema, BillingProfileKindSchema, BillingProfileSchema, BrandingPatchSchema, BrandingSchema, CHECKOUT_ERRORS, CancelSubscriptionInputSchema, CancelSubscriptionResponseSchema, CatalogSchema, CatalogSyncInputSchema, CatalogSyncResultSchema, CheckoutConsentInputSchema, CheckoutPrefillSchema, CheckoutSessionCreateInputSchema, CheckoutSessionCreatedSchema, CheckoutSessionResponseSchema, CheckoutSessionSchema, CheckoutStatusSchema, CodeSchema, ConsentSchema, CurrencySchema, DEFAULT_CHECKOUT_TTL_MINUTES, DEFAULT_ENDPOINT_EVENT_TYPES, DEFAULT_PORTAL_TTL_MINUTES, DOCTOR_PENDING_BACKLOG_MS, DOCTOR_PROVIDER_ENVIRONMENTS, DOCTOR_REQUIRED_BRANDING, DOCTOR_REQUIRED_LEGAL_LINKS, DOCTOR_SEVERITIES, DateTimeSchema, DoctorCatalogSchema, DoctorEmailsSchema, DoctorEndpointSchema, DoctorHostedSchema, DoctorProviderSchema, DoctorReportSchema, DoctorSettingsSchema, DoctorSeveritySchema, DoctorUnsellablePriceSchema, DoctorWarningSchema, EMAIL_LOCALES, EMAIL_TEMPLATES, EMAIL_TEMPLATE_BY_EVENT, ERROR_CATEGORIES, ERROR_CODES, EVENT_DETAIL_SCHEMAS, EVENT_TYPES, EmailExtraTextSchema, EmailSenderSchema, EmailSettingsPatchSchema, EmailSettingsSchema, EmailTemplateSchema, EmailTemplateSettingsSchema, EntitlementSnapshotSchema, EntitlementValueSchema, EntitlementsSchema, ErrorCategorySchema, ErrorResponseSchema, EventIdSchema, EventTypeSchema, ExternalRefSchema, FeatureDefSchema, FeatureTypeSchema, GrantCreateInputSchema, GrantCreateResponseSchema, GrantSchema, HEADER_ACTOR_REF, HEADER_API_VERSION, HEADER_IDEMPOTENCY_KEY, HEADER_REQUEST_ID, HEADER_SDK, IdSchema, IntervalSchema, InvoiceIssueInputSchema, InvoiceIssueResponseSchema, InvoiceLineSchema, InvoiceSchema, InvoiceStatusSchema, InvoiceVoidInputSchema, InvoiceVoidResponseSchema, LEGACY_EVENT_TYPES, LIVE_SUBSCRIPTION_STATUSES, MANUAL_INVOICE_MAX_AMOUNT_MINOR, MAX_CHECKOUT_CONSENT_DOCS, MAX_NOTIFICATION_EMAILS, ManualInvoiceInputSchema, ManualInvoiceLineInputSchema, ManualInvoiceResponseSchema, MetadataSchema, NotificationEmailsSchema, PING_EVENT_TYPE, PLAN_SET_MODES, PORTAL_SESSION_ERRORS, PaymentSchema, PingSchema, PlanDefSchema, PlanSetInputSchema, PlanSetModeSchema, PlanSetResponseSchema, PlanSetWarningSchema, PortalSessionCreateInputSchema, PortalSessionSchema, PriceDefSchema, PriceSchema, ProductSettingsPatchSchema, ProductSettingsSchema, ProviderPayloadSchema, ResyncResultSchema, ReturnOriginSchema, SETTINGS_LOCALES, SettingsLocaleSchema, StatsSchema, StewardApiError, StewardConfigError, StewardContractError, StewardError, StewardNetworkError, SubscriptionSchema, SubscriptionStatusSchema, SubscriptionSummarySchema, TtlMinutesSchema, WEBHOOK_ENDPOINT_STATUSES, WEBHOOK_SECRET_PREFIX, WebUrlSchema, WebhookEndpointCreateInputSchema, WebhookEndpointListSchema, WebhookEndpointRotateInputSchema, WebhookEndpointSchema, WebhookEndpointStatusSchema, WebhookLastDeliverySchema, WebhookSecretSchema, applyEmailSettingsPatch, applySettingsPatch, billingPageUrl, buildDoctorReport, checkoutRedirectUrl, createSteward, defineCatalog, emailLocaleOf, emailReadinessOf, emailRecipients, emailTemplateOf, emailTemplateSettings, endpointDiagnosisOf, errorCodeSpec, flag, generateWebhookSecret, hostedReadinessOf, httpStatusFor, isE164, isEmailRelevant, isEventType, isGrantActive, isKnownErrorCode, isLiveSubscriptionStatus, isReturnUrlAllowed, isValidTckn, isValidVkn, limit, manualInvoiceTotals, resolveEntitlements, returnOriginsOf, splitInclusiveTax, stateCache, text, uniqueEmails, webhookEndpointUrlProblem };
1377
+ //#region src/events.ts
1378
+ /** How long an acknowledged event stays pending: the meter rollup lags behind ingestion (≤ 10 s target). */
1379
+ const EVENTS_RECENT_MS = 6e4;
1380
+ const MAX_BACKOFF_MS = 3e4;
1381
+ const BASE_BACKOFF_MS = 1e3;
1382
+ const WARN_EVERY_MS = 1e4;
1383
+ /** Room for `{"events":[` … `]}` and separators within the ingest body limit. */
1384
+ const BATCH_BYTES = EVENTS_INGEST_MAX_BYTES - 1024;
1385
+ const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1386
+ const describeError = (error) => error instanceof Error ? error.message : String(error);
1387
+ function fieldsOf$1(error) {
1388
+ const { code, retryable, retryAfterMs } = isPlainObject(error) || error instanceof Error ? error : {};
1389
+ return {
1390
+ code: typeof code === "string" ? code : null,
1391
+ retryable: typeof retryable === "boolean" ? retryable : null,
1392
+ retryAfterMs: typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) ? retryAfterMs : null
1393
+ };
1394
+ }
1395
+ function isTransient(error) {
1396
+ const { code, retryable } = fieldsOf$1(error);
1397
+ return retryable === true || code === "invalid_response";
1398
+ }
1399
+ function utf8Length(text) {
1400
+ let bytes = 0;
1401
+ for (let i = 0; i < text.length; i++) {
1402
+ const c = text.charCodeAt(i);
1403
+ if (c < 128) bytes += 1;
1404
+ else if (c < 2048) bytes += 2;
1405
+ else if (c >= 55296 && c <= 56319) {
1406
+ bytes += 4;
1407
+ i++;
1408
+ } else bytes += 3;
1409
+ }
1410
+ return bytes;
1411
+ }
1412
+ function positive(name, value, problems, { integer = false, max = Number.POSITIVE_INFINITY } = {}) {
1413
+ if (!(Number.isFinite(value) && value > 0 && value <= max && (!integer || Number.isInteger(value)))) problems.push(`${name} must be a positive ${integer ? "integer" : "number"}${Number.isFinite(max) ? ` ≤ ${max}` : ""}: ${String(value)}`);
1414
+ }
1415
+ /** Events tracker (M16); see the comment above. */
1416
+ function Events(options) {
1417
+ const flushIntervalMs = options.flushIntervalMs ?? 2e3;
1418
+ const maxBatch = options.maxBatch ?? 500;
1419
+ const maxQueue = options.maxQueue ?? 1e4;
1420
+ const autoFlush = options.autoFlush ?? true;
1421
+ const problems = [];
1422
+ if (typeof options.steward?.events?.ingest !== "function") problems.push("steward.events.ingest is required (createSteward())");
1423
+ positive("flushIntervalMs", flushIntervalMs, problems);
1424
+ positive("maxBatch", maxBatch, problems, {
1425
+ integer: true,
1426
+ max: EVENTS_INGEST_MAX_EVENTS
1427
+ });
1428
+ positive("maxQueue", maxQueue, problems, { integer: true });
1429
+ if (typeof autoFlush !== "boolean") problems.push("autoFlush must be a boolean");
1430
+ if (options.actorRef !== void 0 && !ActorRefSchema.safeParse(options.actorRef).success) problems.push("actorRef format is invalid (kind[:id], e.g. service:api)");
1431
+ if (problems.length > 0) throw new StewardConfigError({ message: `Events: ${problems.join("; ")}` });
1432
+ const { steward, actorRef, onRejected, onDrop } = options;
1433
+ const logger = options.logger ?? console;
1434
+ const now = options.now ?? Date.now;
1435
+ const random = options.random ?? Math.random;
1436
+ /** Waiting events, oldest first. */
1437
+ let queue = [];
1438
+ /** The first `size` queued events were sent with `key` before (a failed batch): the next attempt sends exactly them with it. */
1439
+ let head = null;
1440
+ /** Acknowledged events kept pending for `EVENTS_RECENT_MS`, oldest first. */
1441
+ let recent = [];
1442
+ const byRef = /* @__PURE__ */ new Map();
1443
+ let failures = 0;
1444
+ let retryAt = 0;
1445
+ let timer = null;
1446
+ let chain = Promise.resolve();
1447
+ let runs = 0;
1448
+ let dropWarnedAt = Number.NEGATIVE_INFINITY;
1449
+ let droppedSinceWarn = 0;
1450
+ const index = (entry) => {
1451
+ const ref = entry.event.accountRef;
1452
+ let set = byRef.get(ref);
1453
+ if (set === void 0) {
1454
+ set = /* @__PURE__ */ new Set();
1455
+ byRef.set(ref, set);
1456
+ }
1457
+ set.add(entry);
1458
+ };
1459
+ const unindex = (entry) => {
1460
+ const ref = entry.event.accountRef;
1461
+ const set = byRef.get(ref);
1462
+ if (set === void 0) return;
1463
+ set.delete(entry);
1464
+ if (set.size === 0) byRef.delete(ref);
1465
+ };
1466
+ function hook(name, run) {
1467
+ try {
1468
+ run();
1469
+ } catch (error) {
1470
+ logger.warn(`Events ${name} threw`, { error: describeError(error) });
1471
+ }
1472
+ }
1473
+ function drop(events, info) {
1474
+ if (onDrop !== void 0) hook("onDrop", () => onDrop(events, info));
1475
+ }
1476
+ function pruneRecent() {
1477
+ const oldest = now() - EVENTS_RECENT_MS;
1478
+ let cut = 0;
1479
+ while (cut < recent.length && (recent[cut].ackedAt < oldest || recent.length - cut > maxQueue)) cut++;
1480
+ if (cut === 0) return;
1481
+ for (const entry of recent.slice(0, cut)) unindex(entry);
1482
+ recent = recent.slice(cut);
1483
+ }
1484
+ /** Queue over `maxQueue`: the oldest events go to `onDrop`. A shortened failed batch gets a new key (its composition changed). */
1485
+ function enforceLimit() {
1486
+ const excess = queue.length - maxQueue;
1487
+ if (excess <= 0) return;
1488
+ const dropped = queue.splice(0, excess);
1489
+ for (const entry of dropped) unindex(entry);
1490
+ if (head !== null) {
1491
+ const size = head.size - Math.min(excess, head.size);
1492
+ head = size > 0 ? {
1493
+ key: globalThis.crypto.randomUUID(),
1494
+ size
1495
+ } : null;
1496
+ }
1497
+ droppedSinceWarn += dropped.length;
1498
+ const t = now();
1499
+ if (t - dropWarnedAt >= WARN_EVERY_MS) {
1500
+ logger.warn("Events queue full; the oldest events were dropped (onDrop)", {
1501
+ dropped: droppedSinceWarn,
1502
+ maxQueue
1503
+ });
1504
+ dropWarnedAt = t;
1505
+ droppedSinceWarn = 0;
1506
+ }
1507
+ drop(dropped.map((entry) => entry.event), { reason: "queue_full" });
1508
+ }
1509
+ function takeBatch() {
1510
+ let size = 0;
1511
+ let key;
1512
+ if (head !== null) {
1513
+ size = Math.min(head.size, queue.length);
1514
+ key = head.key;
1515
+ } else {
1516
+ let bytes = 0;
1517
+ while (size < queue.length && size < maxBatch && (size === 0 || bytes + queue[size].bytes + 1 <= BATCH_BYTES)) {
1518
+ bytes += queue[size].bytes + 1;
1519
+ size++;
1520
+ }
1521
+ key = globalThis.crypto.randomUUID();
1522
+ }
1523
+ head = null;
1524
+ const entries = queue.splice(0, size);
1525
+ for (const entry of entries) entry.state = "sending";
1526
+ return {
1527
+ key,
1528
+ entries
1529
+ };
1530
+ }
1531
+ function acknowledge(batch, result) {
1532
+ const rejectedAt = /* @__PURE__ */ new Map();
1533
+ for (const rejection of result.rejected) if (rejection.index >= 0 && rejection.index < batch.entries.length) rejectedAt.set(rejection.index, rejection);
1534
+ const ackedAt = now();
1535
+ const rejected = [];
1536
+ batch.entries.forEach((entry, i) => {
1537
+ const rejection = rejectedAt.get(i);
1538
+ if (rejection !== void 0) {
1539
+ unindex(entry);
1540
+ rejected.push({
1541
+ ...rejection,
1542
+ event: entry.event
1543
+ });
1544
+ return;
1545
+ }
1546
+ entry.state = "acked";
1547
+ entry.ackedAt = ackedAt;
1548
+ recent.push(entry);
1549
+ });
1550
+ pruneRecent();
1551
+ if (rejected.length > 0) {
1552
+ const codes = [...new Set(rejected.map((r) => r.code))];
1553
+ logger.warn("steward rejected events (not retried; onRejected)", {
1554
+ rejected: rejected.length,
1555
+ codes
1556
+ });
1557
+ if (onRejected !== void 0) hook("onRejected", () => onRejected(rejected));
1558
+ }
1559
+ }
1560
+ function backoffMs(retryAfterMs) {
1561
+ const ceiling = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** Math.min(failures - 1, 16));
1562
+ const jittered = Math.round(ceiling / 2 + random() * ceiling / 2);
1563
+ return Math.min(MAX_BACKOFF_MS, Math.max(jittered, retryAfterMs ?? 0));
1564
+ }
1565
+ /** Sends one batch; `false` when sending should pause (transient failure). */
1566
+ async function send(batch) {
1567
+ try {
1568
+ const result = await steward.events.ingest({ events: batch.entries.map((entry) => entry.event) }, {
1569
+ idempotencyKey: batch.key,
1570
+ ...actorRef !== void 0 ? { actorRef } : {}
1571
+ });
1572
+ failures = 0;
1573
+ retryAt = 0;
1574
+ acknowledge(batch, result);
1575
+ return true;
1576
+ } catch (error) {
1577
+ const { code, retryAfterMs } = fieldsOf$1(error);
1578
+ if (isTransient(error)) {
1579
+ for (const entry of batch.entries) entry.state = "queued";
1580
+ queue = [...batch.entries, ...queue];
1581
+ head = {
1582
+ key: batch.key,
1583
+ size: batch.entries.length
1584
+ };
1585
+ failures += 1;
1586
+ const delay = backoffMs(retryAfterMs);
1587
+ retryAt = now() + delay;
1588
+ logger.warn("steward event ingest failed; the batch stays queued", {
1589
+ code,
1590
+ events: batch.entries.length,
1591
+ queued: queue.length,
1592
+ retryInMs: delay
1593
+ });
1594
+ enforceLimit();
1595
+ return false;
1596
+ }
1597
+ for (const entry of batch.entries) unindex(entry);
1598
+ logger.warn("steward event ingest failed permanently; the batch was dropped (onDrop)", {
1599
+ code,
1600
+ events: batch.entries.length
1601
+ });
1602
+ drop(batch.entries.map((entry) => entry.event), {
1603
+ reason: "request_failed",
1604
+ message: code ?? describeError(error),
1605
+ error
1606
+ });
1607
+ return true;
1608
+ }
1609
+ }
1610
+ async function drain(force) {
1611
+ while (queue.length > 0) {
1612
+ if (!force && now() < retryAt) return;
1613
+ if (!await send(takeBatch())) return;
1614
+ }
1615
+ }
1616
+ function arm(delay) {
1617
+ if (!autoFlush || timer !== null || queue.length === 0) return;
1618
+ timer = setTimeout(() => {
1619
+ timer = null;
1620
+ schedule(false);
1621
+ }, Math.max(0, delay));
1622
+ timer.unref?.();
1623
+ }
1624
+ function rearm() {
1625
+ if (queue.length === 0) return;
1626
+ const t = now();
1627
+ arm(retryAt > t ? retryAt - t : flushIntervalMs);
1628
+ }
1629
+ function schedule(force) {
1630
+ runs += 1;
1631
+ const run = chain.then(() => drain(force)).catch((error) => logger.warn("Events flush failed", { error: describeError(error) })).finally(() => {
1632
+ runs -= 1;
1633
+ rearm();
1634
+ });
1635
+ chain = run;
1636
+ return run;
1637
+ }
1638
+ function normalizedTimestamp(timestamp) {
1639
+ if (timestamp === void 0) return new Date(now()).toISOString();
1640
+ if (!(timestamp instanceof Date)) return timestamp;
1641
+ return Number.isNaN(timestamp.getTime()) ? "Invalid Date" : timestamp.toISOString();
1642
+ }
1643
+ function eventOf(ref, name, metadata, externalId, timestamp) {
1644
+ return {
1645
+ accountRef: ref,
1646
+ name,
1647
+ externalId: externalId === void 0 ? globalThis.crypto.randomUUID() : externalId,
1648
+ timestamp: normalizedTimestamp(timestamp),
1649
+ ...metadata !== void 0 ? { metadata: isPlainObject(metadata) ? { ...metadata } : metadata } : {}
1650
+ };
1651
+ }
1652
+ function issuesOf(error) {
1653
+ return error.issues.map((issue) => `${issue.path.map(String).join(".") || "(event)"}: ${issue.message}`).join("; ");
1654
+ }
1655
+ return {
1656
+ track(ref, name, metadata, trackOptions) {
1657
+ let event = null;
1658
+ try {
1659
+ const given = isPlainObject(trackOptions) ? trackOptions : {};
1660
+ event = eventOf(ref, name, metadata, given.externalId, given.timestamp);
1661
+ const parsed = IngestEventSchema.safeParse(event);
1662
+ if (!parsed.success) {
1663
+ const message = issuesOf(parsed.error);
1664
+ logger.warn("Events.track: invalid event dropped (onDrop)", {
1665
+ name: typeof name === "string" ? name.slice(0, 128) : null,
1666
+ problems: message
1667
+ });
1668
+ drop([event], {
1669
+ reason: "invalid_event",
1670
+ message
1671
+ });
1672
+ return;
1673
+ }
1674
+ const entry = {
1675
+ event: parsed.data,
1676
+ bytes: utf8Length(JSON.stringify(parsed.data)),
1677
+ state: "queued",
1678
+ ackedAt: null
1679
+ };
1680
+ queue.push(entry);
1681
+ index(entry);
1682
+ enforceLimit();
1683
+ if (!autoFlush) return;
1684
+ if (queue.length >= maxBatch && runs === 0 && now() >= retryAt) schedule(false);
1685
+ else arm(flushIntervalMs);
1686
+ } catch (error) {
1687
+ logger.warn("Events.track: event dropped", { error: describeError(error) });
1688
+ if (event !== null) drop([event], {
1689
+ reason: "invalid_event",
1690
+ message: describeError(error)
1691
+ });
1692
+ }
1693
+ },
1694
+ flush() {
1695
+ if (timer !== null) {
1696
+ clearTimeout(timer);
1697
+ timer = null;
1698
+ }
1699
+ return schedule(true);
1700
+ },
1701
+ async ingest(events, ingestOptions = {}) {
1702
+ let input;
1703
+ try {
1704
+ input = { events: (Array.isArray(events) ? events : []).map((e) => eventOf(e.accountRef, e.name, e.metadata, e.externalId, e.timestamp)) };
1705
+ } catch (error) {
1706
+ throw new StewardConfigError({
1707
+ code: "invalid_events",
1708
+ message: `Events.ingest: ${describeError(error)}`,
1709
+ cause: error
1710
+ });
1711
+ }
1712
+ const parsed = EventsIngestInputSchema.safeParse(input);
1713
+ if (!parsed.success) throw new StewardConfigError({
1714
+ code: "invalid_events",
1715
+ message: `Events.ingest: ${issuesOf(parsed.error)}`
1716
+ });
1717
+ const entries = parsed.data.events.map((event) => ({
1718
+ event,
1719
+ bytes: 0,
1720
+ state: "sending",
1721
+ ackedAt: null
1722
+ }));
1723
+ for (const entry of entries) index(entry);
1724
+ try {
1725
+ const result = await steward.events.ingest(parsed.data, {
1726
+ idempotencyKey: ingestOptions.idempotencyKey ?? globalThis.crypto.randomUUID(),
1727
+ ...actorRef !== void 0 ? { actorRef } : {}
1728
+ });
1729
+ const rejected = new Set(result.rejected.map((r) => r.index));
1730
+ const ackedAt = now();
1731
+ entries.forEach((entry, i) => {
1732
+ if (rejected.has(i)) {
1733
+ unindex(entry);
1734
+ return;
1735
+ }
1736
+ entry.state = "acked";
1737
+ entry.ackedAt = ackedAt;
1738
+ recent.push(entry);
1739
+ });
1740
+ pruneRecent();
1741
+ return result;
1742
+ } catch (error) {
1743
+ for (const entry of entries) unindex(entry);
1744
+ throw error;
1745
+ }
1746
+ },
1747
+ pending(ref) {
1748
+ pruneRecent();
1749
+ const set = byRef.get(ref);
1750
+ if (set === void 0) return [];
1751
+ return [...set].map((entry) => ({
1752
+ event: entry.event,
1753
+ ackedAt: entry.state === "acked" ? entry.ackedAt : null
1754
+ }));
1755
+ }
1756
+ };
1757
+ }
1758
+ //#endregion
1759
+ //#region src/meters.ts
1760
+ const FAILURE_BACKOFF_MS = 5e3;
1761
+ const STANDING_ORDER = {
1762
+ ok: 0,
1763
+ low: 1,
1764
+ exhausted: 2
1765
+ };
1766
+ const worse = (a, b) => STANDING_ORDER[b] > STANDING_ORDER[a] ? b : a;
1767
+ const micro = (value) => typeof value === "number" ? parseMicroUnits(value) ?? 0n : 0n;
1768
+ const isStanding = (value) => typeof value === "string" && Object.hasOwn(STANDING_ORDER, value);
1769
+ function fieldsOf(error) {
1770
+ const { code, httpStatus } = typeof error === "object" && error !== null ? error : {};
1771
+ return {
1772
+ code: typeof code === "string" ? code : null,
1773
+ httpStatus: typeof httpStatus === "number" ? httpStatus : null
1774
+ };
1775
+ }
1776
+ function isOutage(error) {
1777
+ const { code, httpStatus } = fieldsOf(error);
1778
+ if (httpStatus === 0) return code === "network_error" || code === "timeout";
1779
+ return httpStatus !== null && (httpStatus >= 500 || httpStatus === 429);
1780
+ }
1781
+ /** Rollup lag of a read (`asOf − rolledUpThrough`, steward's clock), ms; unknown → the whole recent window. */
1782
+ function rollupLagMs(body) {
1783
+ const lag = Date.parse(body.asOf) - Date.parse(body.rolledUpThrough);
1784
+ return Number.isFinite(lag) ? Math.min(Math.max(lag, 0), EVENTS_RECENT_MS) : EVENTS_RECENT_MS;
1785
+ }
1786
+ /** Units the pending events add to a meter whose period consumption is `consumed` (micro units). */
1787
+ function pendingMicroOf(def, events, cutoff, consumed) {
1788
+ let total = 0n;
1789
+ let largest = 0n;
1790
+ const distinct = /* @__PURE__ */ new Set();
1791
+ for (const { event, ackedAt } of events) {
1792
+ if (ackedAt !== null && ackedAt < cutoff) continue;
1793
+ if (!matchesFilter(event, def.filter)) continue;
1794
+ const c = contributionOf(event, def.aggregation);
1795
+ switch (c.kind) {
1796
+ case "count":
1797
+ total += MICRO_UNITS_PER_UNIT;
1798
+ break;
1799
+ case "sum":
1800
+ total += c.micro;
1801
+ break;
1802
+ case "max":
1803
+ if (c.micro > largest) largest = c.micro;
1804
+ break;
1805
+ case "unique": distinct.add(c.value);
1806
+ }
1807
+ }
1808
+ switch (def.aggregation.kind) {
1809
+ case "max": return largest > consumed ? largest - consumed : 0n;
1810
+ case "unique": return BigInt(distinct.size) * MICRO_UNITS_PER_UNIT;
1811
+ default: return total;
1812
+ }
1813
+ }
1814
+ /** Meter reader and gate (M13, M15, M16); see the comment above. */
1815
+ function Meters(options) {
1816
+ const ttlMs = options.ttlMs ?? 1e4;
1817
+ const staleIfErrorMs = options.staleIfErrorMs ?? 6e5;
1818
+ const maxEntries = options.maxEntries ?? 1e4;
1819
+ const policy = options.onUnavailable ?? "allow";
1820
+ const problems = [];
1821
+ if (typeof options.steward?.meters?.read !== "function") problems.push("steward.meters.read is required (createSteward())");
1822
+ if (typeof options.catalog?.toSyncInput !== "function") problems.push("catalog is required (defineCatalog())");
1823
+ if (!(Number.isFinite(ttlMs) && ttlMs >= 0)) problems.push(`ttlMs must be a non-negative number: ${ttlMs}`);
1824
+ if (!(Number.isFinite(staleIfErrorMs) && staleIfErrorMs >= 0)) problems.push(`staleIfErrorMs must be a non-negative number: ${staleIfErrorMs}`);
1825
+ if (!(Number.isInteger(maxEntries) && maxEntries >= 1)) problems.push(`maxEntries must be a positive integer: ${maxEntries}`);
1826
+ if (policy !== "allow" && policy !== "deny" && typeof policy !== "function") problems.push(`onUnavailable must be "allow", "deny" or a function: ${String(policy)}`);
1827
+ if (options.events !== void 0 && typeof options.events.pending !== "function") problems.push("events must be an Events() (pending)");
1828
+ if (options.cache !== void 0 && typeof options.cache.peek !== "function") problems.push("cache must be a stateCache() (peek)");
1829
+ if (problems.length > 0) throw new StewardConfigError({ message: `Meters: ${problems.join("; ")}` });
1830
+ const parsed = CatalogSyncInputSchema.safeParse(options.catalog.toSyncInput());
1831
+ if (!parsed.success) throw new StewardConfigError({
1832
+ code: "invalid_catalog",
1833
+ message: "Meters: the catalog is not valid (defineCatalog)"
1834
+ });
1835
+ const facts = meterCatalogFacts(parsed.data);
1836
+ if (facts.meters.size === 0) throw new StewardConfigError({ message: "Meters: the catalog defines no meters" });
1837
+ const defaultCredits = facts.creditsOf(facts.defaultPlan);
1838
+ const { steward, cache, events, onFallback } = options;
1839
+ const logger = options.logger ?? console;
1840
+ const now = options.now ?? Date.now;
1841
+ const backoffMs = Math.min(ttlMs, FAILURE_BACKOFF_MS);
1842
+ const entries = /* @__PURE__ */ new Map();
1843
+ const flights = /* @__PURE__ */ new Map();
1844
+ const warnedMissing = /* @__PURE__ */ new Set();
1845
+ function put(ref, entry) {
1846
+ entries.delete(ref);
1847
+ entries.set(ref, entry);
1848
+ for (const oldest of entries.keys()) {
1849
+ if (entries.size <= maxEntries) break;
1850
+ entries.delete(oldest);
1851
+ }
1852
+ }
1853
+ function degrade(ref, entry, failure) {
1854
+ const t = now();
1855
+ if (entry.body !== null && t < entry.confirmedAt + ttlMs + staleIfErrorMs) {
1856
+ if (failure.warned !== "stale") {
1857
+ const { code } = fieldsOf(failure.error);
1858
+ logger.warn("steward meters unreadable; using the stale value", {
1859
+ ref,
1860
+ code,
1861
+ ageMs: t - entry.confirmedAt
1862
+ });
1863
+ failure.warned = "stale";
1864
+ }
1865
+ return {
1866
+ kind: "value",
1867
+ body: entry.body,
1868
+ bodyAt: entry.bodyAt,
1869
+ source: "stale"
1870
+ };
1871
+ }
1872
+ return {
1873
+ kind: "fallback",
1874
+ error: failure.error
1875
+ };
1876
+ }
1877
+ async function load(ref, flight) {
1878
+ const known = entries.get(ref);
1879
+ let read;
1880
+ try {
1881
+ read = await steward.meters.read(ref, known?.body && known.etag !== null ? { etag: known.etag } : {});
1882
+ } catch (error) {
1883
+ if (!isOutage(error)) throw error;
1884
+ const failure = {
1885
+ error,
1886
+ until: now() + backoffMs,
1887
+ warned: null
1888
+ };
1889
+ const current = entries.get(ref) ?? {
1890
+ body: null,
1891
+ etag: null,
1892
+ bodyAt: Number.NEGATIVE_INFINITY,
1893
+ confirmedAt: Number.NEGATIVE_INFINITY,
1894
+ expiresAt: Number.NEGATIVE_INFINITY,
1895
+ failure: null
1896
+ };
1897
+ current.failure = failure;
1898
+ put(ref, current);
1899
+ return degrade(ref, current, failure);
1900
+ }
1901
+ const t = now();
1902
+ let body;
1903
+ let bodyAt;
1904
+ if (!read.notModified) {
1905
+ body = read.meters;
1906
+ bodyAt = t;
1907
+ } else if (known?.body) {
1908
+ body = known.body;
1909
+ bodyAt = known.bodyAt;
1910
+ } else throw new StewardContractError({ httpStatus: 304 });
1911
+ put(ref, {
1912
+ body,
1913
+ etag: read.etag,
1914
+ bodyAt,
1915
+ confirmedAt: t,
1916
+ expiresAt: flight.invalidated ? Number.NEGATIVE_INFINITY : t + ttlMs,
1917
+ failure: null
1918
+ });
1919
+ return {
1920
+ kind: "value",
1921
+ body,
1922
+ bodyAt,
1923
+ source: "live"
1924
+ };
1925
+ }
1926
+ function join(ref) {
1927
+ const running = flights.get(ref);
1928
+ if (running) return running.promise;
1929
+ const control = { invalidated: false };
1930
+ const promise = load(ref, control).finally(() => {
1931
+ if (flights.get(ref)?.promise === promise) flights.delete(ref);
1932
+ });
1933
+ flights.set(ref, Object.assign(control, { promise }));
1934
+ return promise;
1935
+ }
1936
+ async function snapshot(ref, fresh) {
1937
+ const entry = entries.get(ref);
1938
+ if (entry) put(ref, entry);
1939
+ if (entry && !fresh) {
1940
+ const t = now();
1941
+ if (entry.body !== null && t < entry.expiresAt) return {
1942
+ kind: "value",
1943
+ body: entry.body,
1944
+ bodyAt: entry.bodyAt,
1945
+ source: "cached"
1946
+ };
1947
+ if (entry.failure && t < entry.failure.until) return degrade(ref, entry, entry.failure);
1948
+ }
1949
+ return join(ref);
1950
+ }
1951
+ /** Steward's meter plus the pending units. `def` absent: a meter this catalog does not define (no pending units). */
1952
+ function computed(meter, def, pending, cutoff, source) {
1953
+ const credited = micro(meter.creditedUnits);
1954
+ const consumedBefore = micro(meter.consumedUnits);
1955
+ const pendingMicro = def === void 0 ? 0n : pendingMicroOf(def, pending, cutoff, consumedBefore);
1956
+ const consumed = consumedBefore + pendingMicro;
1957
+ const balance = credited - consumed;
1958
+ const standing = def === void 0 || pendingMicro === 0n ? meter.standing : worse(meter.standing, standingOf({
1959
+ creditedMicro: credited,
1960
+ consumedMicro: consumed,
1961
+ lowAt: def.lowAt
1962
+ }));
1963
+ return {
1964
+ balanceMicro: balance,
1965
+ value: {
1966
+ periodStart: meter.periodStart,
1967
+ periodEnd: meter.periodEnd,
1968
+ includedUnits: meter.includedUnits,
1969
+ consumedUnits: fromMicroUnits(consumed),
1970
+ creditedUnits: fromMicroUnits(credited),
1971
+ balance: fromMicroUnits(balance),
1972
+ overageUnits: fromMicroUnits(balance < 0n ? -balance : 0n),
1973
+ pendingUnits: fromMicroUnits(pendingMicro),
1974
+ standing,
1975
+ billable: meter.billable,
1976
+ amountDueMinor: meter.amountDueMinor,
1977
+ currency: meter.currency,
1978
+ capReached: meter.billable && meter.capReached === true,
1979
+ source
1980
+ }
1981
+ };
1982
+ }
1983
+ /** Fallback value: the account's coarse facts from `cache` if it holds them, else the default plan's credit; zero consumption. */
1984
+ function fallbackOf(ref, code, pending) {
1985
+ const def = facts.meters.get(code);
1986
+ const coarse = cache?.peek(ref)?.meters?.[code];
1987
+ const includedMicro = coarse !== void 0 ? coarse.includedUnits === null ? null : micro(coarse.includedUnits) : defaultCredits.get(code) ?? null;
1988
+ const credited = includedMicro ?? 0n;
1989
+ const consumed = pendingMicroOf(def, pending, Number.NEGATIVE_INFINITY, 0n);
1990
+ const balance = credited - consumed;
1991
+ const standing = worse(isStanding(coarse?.standing) ? coarse.standing : "ok", standingOf({
1992
+ creditedMicro: credited,
1993
+ consumedMicro: consumed,
1994
+ lowAt: def.lowAt
1995
+ }));
1996
+ return {
1997
+ balanceMicro: balance,
1998
+ value: {
1999
+ periodStart: null,
2000
+ periodEnd: null,
2001
+ includedUnits: includedMicro === null ? null : fromMicroUnits(includedMicro),
2002
+ consumedUnits: fromMicroUnits(consumed),
2003
+ creditedUnits: fromMicroUnits(credited),
2004
+ balance: fromMicroUnits(balance),
2005
+ overageUnits: fromMicroUnits(balance < 0n ? -balance : 0n),
2006
+ pendingUnits: fromMicroUnits(consumed),
2007
+ standing,
2008
+ billable: coarse?.billable === true,
2009
+ amountDueMinor: null,
2010
+ currency: null,
2011
+ capReached: false,
2012
+ source: "fallback"
2013
+ }
2014
+ };
2015
+ }
2016
+ function fellBack(event) {
2017
+ if (onFallback === void 0) return;
2018
+ try {
2019
+ onFallback(event);
2020
+ } catch (error) {
2021
+ logger.warn("Meters onFallback threw", {
2022
+ ref: event.ref,
2023
+ error: String(error)
2024
+ });
2025
+ }
2026
+ }
2027
+ function warnUnavailable(ref, snap) {
2028
+ const failure = entries.get(ref)?.failure;
2029
+ if (failure && failure.error === snap.error) {
2030
+ if (failure.warned === "fallback") return;
2031
+ failure.warned = "fallback";
2032
+ }
2033
+ const { code } = fieldsOf(snap.error);
2034
+ logger.warn("steward meters unreadable, no stale value; using the catalog fallback", {
2035
+ ref,
2036
+ code
2037
+ });
2038
+ }
2039
+ /** One meter of a snapshot; a catalog meter steward does not report gets the catalog value. */
2040
+ function meterOf(ref, code, snap, pending) {
2041
+ const def = facts.meters.get(code);
2042
+ const reported = Object.hasOwn(snap.body.meters, code) ? snap.body.meters[code] : void 0;
2043
+ if (reported !== void 0) return computed(reported, def, pending, snap.bodyAt - rollupLagMs(snap.body), snap.source);
2044
+ if (!warnedMissing.has(code)) {
2045
+ warnedMissing.add(code);
2046
+ logger.warn("steward does not report a catalog meter (catalog not pushed?); using the catalog value", { meter: code });
2047
+ }
2048
+ fellBack({
2049
+ ref,
2050
+ meter: code,
2051
+ reason: "meter_not_found"
2052
+ });
2053
+ return fallbackOf(ref, code, pending);
2054
+ }
2055
+ function requireMeter(name, meter) {
2056
+ if (typeof meter !== "string" || !facts.meters.has(meter)) throw new StewardConfigError({
2057
+ code: "unknown_meter",
2058
+ message: `Meters.${name}: not a catalog meter: ${JSON.stringify(meter)}`
2059
+ });
2060
+ }
2061
+ const pendingOf = (ref) => events?.pending(ref) ?? [];
2062
+ return {
2063
+ async get(ref, readOptions = {}) {
2064
+ const snap = await snapshot(ref, readOptions.fresh === true);
2065
+ const pending = pendingOf(ref);
2066
+ const out = {};
2067
+ if (snap.kind === "fallback") {
2068
+ warnUnavailable(ref, snap);
2069
+ fellBack({
2070
+ ref,
2071
+ meter: null,
2072
+ reason: fieldsOf(snap.error).code ?? "unknown"
2073
+ });
2074
+ for (const code of facts.meters.keys()) out[code] = fallbackOf(ref, code, pending).value;
2075
+ return out;
2076
+ }
2077
+ for (const code of facts.meters.keys()) out[code] = meterOf(ref, code, snap, pending).value;
2078
+ for (const [code, meter] of Object.entries(snap.body.meters)) if (!Object.hasOwn(out, code)) out[code] = computed(meter, void 0, pending, 0, snap.source).value;
2079
+ return out;
2080
+ },
2081
+ async balance(ref, meter, readOptions = {}) {
2082
+ requireMeter("balance", meter);
2083
+ const snap = await snapshot(ref, readOptions.fresh === true);
2084
+ const pending = pendingOf(ref);
2085
+ if (snap.kind === "value") return meterOf(ref, meter, snap, pending).value;
2086
+ warnUnavailable(ref, snap);
2087
+ fellBack({
2088
+ ref,
2089
+ meter,
2090
+ reason: fieldsOf(snap.error).code ?? "unknown"
2091
+ });
2092
+ return fallbackOf(ref, meter, pending).value;
2093
+ },
2094
+ async check(ref, meter, checkOptions = {}) {
2095
+ requireMeter("check", meter);
2096
+ const units = checkOptions.units ?? 1;
2097
+ const unitsMicro = typeof units === "number" ? parseMicroUnits(units) : null;
2098
+ if (unitsMicro === null) throw new StewardConfigError({
2099
+ code: "invalid_units",
2100
+ message: `Meters.check: units must be a finite, non-negative number: ${String(units)}`
2101
+ });
2102
+ const snap = await snapshot(ref, checkOptions.fresh === true);
2103
+ const pending = pendingOf(ref);
2104
+ const result = ({ value, balanceMicro }, allowed) => ({
2105
+ allowed: allowed ?? (value.billable ? !value.capReached : value.includedUnits === null || balanceMicro >= unitsMicro),
2106
+ balance: value.balance,
2107
+ standing: value.standing,
2108
+ billable: value.billable,
2109
+ source: value.source
2110
+ });
2111
+ if (snap.kind === "value") return result(meterOf(ref, meter, snap, pending));
2112
+ warnUnavailable(ref, snap);
2113
+ const fallback = fallbackOf(ref, meter, pending);
2114
+ let allowed;
2115
+ if (typeof policy === "function") try {
2116
+ allowed = policy({
2117
+ ref,
2118
+ meter,
2119
+ units,
2120
+ balance: fallback.value,
2121
+ error: snap.error
2122
+ }) === true;
2123
+ } catch (error) {
2124
+ logger.warn("Meters onUnavailable threw; the steward error is thrown", {
2125
+ ref,
2126
+ meter,
2127
+ error: String(error)
2128
+ });
2129
+ throw snap.error;
2130
+ }
2131
+ else allowed = policy === "allow";
2132
+ fellBack({
2133
+ ref,
2134
+ meter,
2135
+ reason: fieldsOf(snap.error).code ?? "unknown"
2136
+ });
2137
+ return result(fallback, allowed);
2138
+ },
2139
+ invalidate(ref) {
2140
+ const flight = flights.get(ref);
2141
+ if (flight) {
2142
+ flight.invalidated = true;
2143
+ flights.delete(ref);
2144
+ }
2145
+ const entry = entries.get(ref);
2146
+ if (entry) Object.assign(entry, {
2147
+ expiresAt: Number.NEGATIVE_INFINITY,
2148
+ failure: null
2149
+ });
2150
+ }
2151
+ };
2152
+ }
2153
+ //#endregion
2154
+ export { ACCOUNT_INCLUDES, API_VERSION, AccessStateSchema, AccountBenefitSchema, AccountDeleteResponseSchema, AccountDetailSchema, AccountDunningStateSchema, AccountLocaleSchema, AccountMeterSchema, AccountMeterStateSchema, AccountMetersSchema, AccountMetersStateSchema, AccountOpenCheckoutSchema, AccountOpenInvoiceSchema, AccountProfileStateSchema, AccountScheduledChangeSchema, AccountSchema, AccountStateSchema, AccountSubscriptionStateSchema, AccountUpsertInputSchema, AccountUpsertResponseSchema, AccountsRefreshResultSchema, ActorRefSchema, AmountMinorSchema, AnyBillingEventSchema, ApiVersionSchema, BENEFIT_TYPES, BILLING_PATH_PLACEHOLDERS, BankTransferInstructionsEmailSchema, BankTransferPaymentDaysSchema, BankTransferRecordInputSchema, BankTransferRecordedSchema, BankTransferSettingsPatchSchema, BankTransferSettingsSchema, BenefitDefSchema, BenefitDescriptionSchema, BenefitSourceSchema, BenefitTypeSchema, BillingEventSchema, BillingPathSchema, BillingProfileKindSchema, BillingProfileSchema, BillingSettingsPatchSchema, BillingSettingsSchema, BrandingPatchSchema, BrandingSchema, CHECKOUT_ERRORS, CancelSubscriptionInputSchema, CancelSubscriptionResponseSchema, CatalogBenefitSchema, CatalogMeterSchema, CatalogPlanSchema, CatalogSchema, CatalogSyncInputSchema, CatalogSyncResultSchema, ChangeSubscriptionResponseSchema, CheckoutConsentInputSchema, CheckoutPrefillSchema, CheckoutSessionCreateInputSchema, CheckoutSessionCreatedSchema, CheckoutSessionResponseSchema, CheckoutSessionSchema, CheckoutStatusSchema, CodeSchema, ConsentSchema, CreditGrantInputSchema, CreditGrantResponseSchema, CurrencySchema, DEFAULT_BANK_TRANSFER_PAYMENT_DAYS, DEFAULT_CHECKOUT_TTL_MINUTES, DEFAULT_ENDPOINT_EVENT_TYPES, DEFAULT_EVENT_RETENTION_DAYS, DEFAULT_LATE_GRACE_MINUTES, DEFAULT_METER_CHARGE_MODE, DEFAULT_METER_LOW_AT, DEFAULT_MIN_INVOICE_MINOR, DEFAULT_PAYMENT_DUE_DAYS, DEFAULT_PORTAL_TTL_MINUTES, DOCTOR_PENDING_BACKLOG_MS, DOCTOR_PROVIDER_ENVIRONMENTS, DOCTOR_REQUIRED_BRANDING, DOCTOR_REQUIRED_LEGAL_LINKS, DOCTOR_ROLLUP_LAG_WARN_SECONDS, DOCTOR_SEVERITIES, DateTimeSchema, DecimalUnitsStringSchema, DoctorBillingSchema, DoctorCatalogSchema, DoctorEmailsSchema, DoctorEndpointSchema, DoctorEventsSchema, DoctorHostedSchema, DoctorOverageSchema, DoctorProviderSchema, DoctorReportSchema, DoctorSettingsSchema, DoctorSeveritySchema, DoctorUnsellablePriceSchema, DoctorWarningSchema, EMAIL_LOCALES, EMAIL_PAYLOAD_SCHEMAS, EMAIL_TEMPLATES, EMAIL_TEMPLATE_BY_EVENT, ERROR_CATEGORIES, ERROR_CODES, EVENTS_INGEST_MAX_BYTES, EVENTS_INGEST_MAX_EVENTS, EVENTS_RECENT_MS, EVENT_DETAIL_SCHEMAS, EVENT_METADATA_KEY_MAX_LENGTH, EVENT_METADATA_MAX_PAIRS, EVENT_METADATA_STRING_MAX_LENGTH, EVENT_NAME_MAX_LENGTH, EVENT_TIMESTAMP_MAX_AGE_DAYS, EVENT_TIMESTAMP_MAX_SKEW_MINUTES, EVENT_TYPES, EmailExtraTextSchema, EmailSenderSchema, EmailSettingsPatchSchema, EmailSettingsSchema, EmailTemplateSchema, EmailTemplateSettingsSchema, EntitlementSnapshotSchema, EntitlementValueSchema, EntitlementsSchema, ErrorCategorySchema, ErrorResponseSchema, EventExternalIdSchema, EventIdSchema, EventMetadataKeySchema, EventMetadataSchema, EventMetadataValueSchema, EventNameSchema, EventRetentionDaysSchema, EventTypeSchema, Events, EventsIngestInputSchema, EventsIngestResultSchema, EventsSettingsPatchSchema, EventsSettingsSchema, ExternalRefSchema, FEATURE_FLAG_BENEFIT_TYPE, FULL_STATE_EVENT_TYPES, FeatureDefSchema, FeatureFlagBenefitDefSchema, FeatureFlagPropertiesSchema, FeatureTypeSchema, GrantCreateInputSchema, GrantCreateResponseSchema, GrantSchema, HEADER_ACTOR_REF, HEADER_API_VERSION, HEADER_IDEMPOTENCY_KEY, HEADER_REQUEST_ID, HEADER_SDK, INDIVIDUAL_IDENTITY_PLACEHOLDER, INGEST_REJECT_CODES, INVOICE_BILLING_REASONS, INVOICE_PAYMENT_STATUSES, IdSchema, IngestEventSchema, IngestRejectCodeSchema, IngestRejectionSchema, IntervalSchema, InvalidUnitValueError, InvoiceBillingReasonSchema, InvoiceIssueInputSchema, InvoiceIssueResponseSchema, InvoiceLineSchema, InvoicePaymentDueEmailSchema, InvoicePaymentSessionCreateInputSchema, InvoicePaymentSessionSchema, InvoicePaymentStatusSchema, InvoiceSchema, InvoiceStatusSchema, InvoiceVoidInputSchema, InvoiceVoidResponseSchema, InvoiceWaiveInputSchema, InvoiceWaiveResponseSchema, LEGACY_EVENT_TYPES, LIVE_SUBSCRIPTION_STATUSES, LateGraceMinutesSchema, MANUAL_INVOICE_MAX_AMOUNT_MINOR, MAX_CATALOG_BENEFITS, MAX_CATALOG_METERED_PRICES, MAX_CATALOG_METERS, MAX_CHECKOUT_CONSENT_DOCS, MAX_GRANT_BENEFITS, MAX_METERED_PRICE_PER_UNITS, MAX_METER_UNITS, MAX_NOTIFICATION_EMAILS, MAX_PLAN_BENEFITS, METERED_PRICE_TAX_MISMATCH, METER_AGGREGATION_KINDS, METER_CHARGE_MODES, METER_CHARGE_STATUSES, METER_CREDIT_ENTRY_KINDS, METER_FILTER_MAX_CONDITIONS, METER_FILTER_MAX_DEPTH, METER_FILTER_OPERATORS, METER_PERIOD_STATUSES, METER_STANDINGS, MICRO_UNITS_PER_UNIT, ManualInvoiceInputSchema, ManualInvoiceLineInputSchema, ManualInvoiceResponseSchema, MeResponseSchema, MetadataSchema, MeterAggregationSchema, MeterChargeLineSchema, MeterChargeListSchema, MeterChargeModeSchema, MeterChargeSchema, MeterChargeStatusSchema, MeterCreditBenefitDefSchema, MeterCreditEntryKindSchema, MeterCreditEntrySchema, MeterCreditPropertiesSchema, MeterDefSchema, MeterFilterSchema, MeterLabelSchema, MeterLowAtSchema, MeterPeriodListSchema, MeterPeriodSchema, MeterPeriodStatusSchema, MeterStandingSchema, MeterUnitsSchema, MeteredPriceDefSchema, MeteredPriceSchema, MeteringSettingsPatchSchema, MeteringSettingsSchema, Meters, NoticedMeteredRateSchema, NotificationEmailsSchema, OVERRIDE_BENEFIT_SOURCE, PAYMENT_METHODS, PING_EVENT_TYPE, PLAN_SET_MODES, PORTAL_SESSION_ERRORS, PaymentDueDaysSchema, PaymentMethodSchema, PaymentSchema, PingSchema, PlanDefSchema, PlanSetInputSchema, PlanSetModeSchema, PlanSetResponseSchema, PlanSetWarningSchema, PortalSessionCreateInputSchema, PortalSessionSchema, PriceChangeNoticeEmailSchema, PriceChangeNoticeMeteredRateSchema, PriceDefSchema, PriceSchema, ProductSettingsPatchSchema, ProductSettingsSchema, ProviderPayloadSchema, ResyncResultSchema, ReturnOriginSchema, SETTINGS_LOCALES, STEWARD_CAPABILITIES, SettingsLocaleSchema, StatsSchema, StewardApiError, StewardConfigError, StewardContractError, StewardError, StewardNetworkError, SubscriptionChangeInputSchema, SubscriptionSchema, SubscriptionStatusSchema, SubscriptionSummarySchema, TtlMinutesSchema, UNIT_DECIMALS, UncancelSubscriptionResponseSchema, WEBHOOK_ENDPOINT_STATUSES, WEBHOOK_SECRET_PREFIX, WebUrlSchema, WebhookEndpointCreateInputSchema, WebhookEndpointListSchema, WebhookEndpointRotateInputSchema, WebhookEndpointSchema, WebhookEndpointStatusSchema, WebhookLastDeliverySchema, WebhookSecretSchema, addUtcMonths, applyEmailSettingsPatch, applySettingsPatch, billingPageUrl, billingSettingsOf, buildDoctorReport, canonicalMeterFilter, checkoutRedirectUrl, consumptionOf, contributionOf, count, createSteward, defineCatalog, emailLocaleOf, emailReadinessOf, emailRecipients, emailTemplateOf, emailTemplateSettings, endpointDiagnosisOf, errorCodeSpec, eventsSettingsOf, featureFlag, featureValueMatchesType, flag, fromMicroUnits, generateWebhookSecret, hasControlCharacters, hostedReadinessOf, httpStatusFor, isE164, isEmailRelevant, isEventTimestampInRange, isEventType, isFeatureFlagBenefit, isGrantActive, isKnownErrorCode, isLiveSubscriptionStatus, isMeterCreditBenefit, isMicroPrecise, isReturnUrlAllowed, isValidTckn, isValidVkn, limit, liveSubscriptionOf, manualInvoiceTotals, matchesFilter, max, meter, meterChargeTotals, meterCredit, meterCreditsOf, meterFilterClauseOf, meteringSettingsOf, microToDecimalString, overageCapReached, overageOf, parseMicroUnits, periodBoundsOf, plainDecimalOf, rateOverage, resolveBenefits, resolveEntitlements, returnOriginsOf, rolloverOf, splitInclusiveTax, standingOf, stateCache, sum, text, toMicroUnits, unique, uniqueEmails, webhookEndpointUrlProblem };