@stewardhq/sdk 0.1.0-rc.0 → 0.3.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.
@@ -1,4 +1,4 @@
1
- import { A as CodeSchema, B as IntervalSchema, C as BillingProfileSchema, F as EntitlementsSchema, M as DateTimeSchema, O as ActorRefSchema, R as ExternalRefSchema, S as BillingProfileKindSchema, T as SubscriptionStatusSchema, a as EVENT_TYPES, j as CurrencySchema, k as AmountMinorSchema, o as EventIdSchema, r as BillingEventSchema, v as AccountSchema, y as AccountStateSchema, z as IdSchema } from "./events.js";
1
+ import { $ as CurrencySchema, C as BillingProfileKindSchema, E as SubscriptionStatusSchema, F as EmailSettingsPatchSchema, I as EmailSettingsSchema, Q as CodeSchema, V as applyEmailSettingsPatch, X as ActorRefSchema, Z as AmountMinorSchema, a as EVENT_TYPES, b as AccountStateSchema, ct as IntervalSchema, et as DateTimeSchema, j as EMAIL_TEMPLATES, o as EventIdSchema, ot as ExternalRefSchema, rt as EntitlementsSchema, st as IdSchema, w as BillingProfileSchema, y as AccountSchema } from "./events.js";
2
2
  import { z } from "zod";
3
3
  //#region ../contract/src/catalog.ts
4
4
  const FeatureTypeSchema = z.enum([
@@ -175,7 +175,9 @@ const ProductSettingsSchema = z.object({
175
175
  branding: BrandingSchema.default({}),
176
176
  /** Açık checkout oturumunun ömrü (sağlayıcının form ömrü daha kısaysa o geçerli). */
177
177
  checkoutTtlMinutes: TtlMinutesSchema.default(30),
178
- portalTtlMinutes: TtlMinutesSchema.default(30)
178
+ portalTtlMinutes: TtlMinutesSchema.default(30),
179
+ /** Billing emails sent by steward (`emails.ts`). */
180
+ emails: EmailSettingsSchema.default({ templates: {} })
179
181
  });
180
182
  const BrandingPatchSchema = z.object({
181
183
  name: BrandingNameSchema.nullable().optional(),
@@ -193,7 +195,8 @@ const ProductSettingsPatchSchema = z.object({
193
195
  termsUrl: WebUrlSchema.nullable().optional(),
194
196
  branding: BrandingPatchSchema.nullable().optional(),
195
197
  checkoutTtlMinutes: TtlMinutesSchema.optional(),
196
- portalTtlMinutes: TtlMinutesSchema.optional()
198
+ portalTtlMinutes: TtlMinutesSchema.optional(),
199
+ emails: EmailSettingsPatchSchema.nullable().optional()
197
200
  }).strict();
198
201
  /**
199
202
  * Doğrulanmış yamayı ayarlara uygular (saf). Sonuç şemadan geçer; alan kuralları
@@ -210,7 +213,8 @@ function applySettingsPatch(current, patch) {
210
213
  else if (fieldValue !== void 0) branding[field] = fieldValue;
211
214
  }
212
215
  next.branding = branding;
213
- } else if (value === null) delete next[key];
216
+ } else if (key === "emails") next.emails = applyEmailSettingsPatch(current.emails, value);
217
+ else if (value === null) delete next[key];
214
218
  else next[key] = value;
215
219
  }
216
220
  return ProductSettingsSchema.parse(next);
@@ -228,117 +232,52 @@ function isReturnUrlAllowed(settings, returnUrl) {
228
232
  //#endregion
229
233
  //#region ../contract/src/checkout.ts
230
234
  const ReturnUrlSchema = z.url({ protocol: /^https?$/ });
231
- /** Kabul edilen sözleşme — içerik üründe yaşar, burada yalnızca iz. */
235
+ /** Accepted document — content lives in the product, steward keeps only the trace. */
232
236
  const ConsentSchema = z.object({
233
237
  document: z.string().min(1).max(100),
234
238
  version: z.string().min(1).max(100),
235
239
  acceptedAt: DateTimeSchema,
236
- /** Belgenin bağı (hosted akışta her zaman; eski gövdede verildiyse). */
240
+ /** Document link. */
237
241
  url: z.string().optional()
238
242
  });
239
- /** Checkout gövdesindeki onay: hosted akışta onaylatılacak belge, eski gövdede kabul izi. */
243
+ /** Document the hosted page asks the customer to accept (the page records acceptance). */
240
244
  const CheckoutConsentInputSchema = z.object({
241
245
  document: z.string().min(1).max(100),
242
246
  version: z.string().min(1).max(100),
243
- /** Hosted akışta zorunlu: sayfadaki belge bağı. */
244
- url: z.url({ protocol: /^https?$/ }).max(2e3).optional(),
245
- /** Yalnızca eski gövdede (ve orada zorunlu): ürün onayı kendisi aldı. */
246
- acceptedAt: DateTimeSchema.optional()
247
+ url: z.url({ protocol: /^https?$/ }).max(2e3)
247
248
  });
248
- /** Hosted sayfanın ön doldurması (profil/kimlik verisi değil; K21). */
249
+ /** Hosted page prefill (not profile/identity data; K21). */
249
250
  const CheckoutPrefillSchema = z.object({
250
251
  email: z.email().max(200).optional(),
251
252
  name: z.string().trim().min(1).max(200).optional(),
252
253
  kind: BillingProfileKindSchema.optional()
253
254
  });
254
- /** Hosted akışta onaylatılabilecek en çok belge sayısı. */
255
+ /** Maximum number of documents the hosted page can ask the customer to accept. */
255
256
  const MAX_CHECKOUT_CONSENT_DOCS = 20;
256
257
  const CheckoutSessionCreateInputSchema = z.object({
257
258
  planCode: CodeSchema,
258
259
  interval: IntervalSchema,
259
- /** Verilmezse ürün ayarı `defaultCurrency` (eski gövdede zorunlu). */
260
+ /** Defaults to the product setting `defaultCurrency`. */
260
261
  currency: CurrencySchema.optional(),
261
- /** Hosted: `tr` | `en`, verilmezse ürün ayarı `defaultLocale`. Eski gövdede zorunlu, iki harf. */
262
- locale: z.string().regex(/^[a-z]{2}$/).optional(),
262
+ /** `tr` | `en`; defaults to the product setting `defaultLocale`. */
263
+ locale: z.enum(SETTINGS_LOCALES).optional(),
263
264
  /**
264
- * Sonuç bu adrese döner. Adreste yer tutucu (`{CHECKOUT_ID}`, `{STATUS}`,
265
- * `{ACCOUNT_REF}`, `{LOCALE}`) varsa (yalnızca hosted) steward yalnızca onları
266
- * doldurur, parametre eklemez; yoksa `?session=<id>&status=success|failed|pending`
267
- * eklenir. pending: sağlayıcıya ulaşılamadı, oturum açık; ürün "işleniyor" gösterir.
268
- * Origin'i ürün ayarlarında izinli olmalı (`appUrl` + `extraReturnOrigins`).
265
+ * The outcome returns here. Placeholders (`{CHECKOUT_ID}`, `{STATUS}`, `{ACCOUNT_REF}`,
266
+ * `{LOCALE}`) are filled if present; query parameters are never appended. `STATUS` is
267
+ * success | failed | pending (pending: provider unreachable, the session stays open and the
268
+ * product shows "processing"). The origin must be allowed by product settings
269
+ * (`appUrl` + `extraReturnOrigins`).
269
270
  */
270
- successUrl: ReturnUrlSchema.optional(),
271
- /** `successUrl`in eski adı. */
272
- returnUrl: ReturnUrlSchema.optional(),
273
- /** Yalnızca hosted: sayfadaki "Vazgeç" hedefi (yoksa ürün ayarı `appUrl`). Origin kuralı `successUrl`le aynı. */
271
+ successUrl: ReturnUrlSchema,
272
+ /** "Cancel" target on the page (defaults to product setting `appUrl`). Same origin rule as `successUrl`. */
274
273
  cancelUrl: ReturnUrlSchema.optional(),
275
- /** Hesap yoksa bu adla açılır; hesap varsa adı değiştirmez. */
274
+ /** Opens the account with this name if it does not exist; never renames an existing account. */
276
275
  displayName: z.string().min(1).max(200).optional(),
277
- /** Yalnızca hosted. */
278
276
  prefill: CheckoutPrefillSchema.optional(),
279
- consents: z.array(CheckoutConsentInputSchema).optional(),
280
- /** Yalnızca eski gövde. */
281
- profile: BillingProfileSchema.optional(),
282
- /**
283
- * Yalnızca eski gövde: sağlayıcının istediği kimlik numarası (ör. TCKN/VKN).
284
- * Yalnızca sağlayıcıya iletilir; servis bunu SAKLAMAZ ve LOGLAMAZ.
285
- */
286
- identityNumber: z.string().regex(/^\d{10,11}$/).optional()
277
+ consents: z.array(CheckoutConsentInputSchema).max(20).optional()
287
278
  }).superRefine((body, ctx) => {
288
- const issue = (path, message) => ctx.addIssue({
289
- code: "custom",
290
- path: [path],
291
- message
292
- });
293
- if (body.successUrl !== void 0 && body.returnUrl !== void 0) issue("returnUrl", "successUrl ve returnUrl (eski ad) birlikte verilemez");
294
- if (body.successUrl === void 0 && body.returnUrl === void 0) issue("successUrl", "zorunlu");
295
- const hasProfile = body.profile !== void 0;
296
- if (hasProfile !== (body.identityNumber !== void 0)) {
297
- issue(hasProfile ? "identityNumber" : "profile", "eski gövde profile ve identityNumber'ı birlikte taşır; hosted checkout ikisini de taşımaz");
298
- return;
299
- }
300
- if (hasProfile) {
301
- if (body.currency === void 0) issue("currency", "eski gövdede zorunlu");
302
- if (body.locale === void 0) issue("locale", "eski gövdede zorunlu");
303
- if (body.consents === void 0) issue("consents", "eski gövdede zorunlu");
304
- body.consents?.forEach((consent, i) => {
305
- if (consent.acceptedAt === void 0) ctx.addIssue({
306
- code: "custom",
307
- path: [
308
- "consents",
309
- i,
310
- "acceptedAt"
311
- ],
312
- message: "eski gövdede zorunlu"
313
- });
314
- });
315
- if (body.cancelUrl !== void 0) issue("cancelUrl", "yalnızca hosted checkout");
316
- if (body.prefill !== void 0) issue("prefill", "yalnızca hosted checkout");
317
- return;
318
- }
319
- if (body.locale !== void 0 && !SETTINGS_LOCALES.includes(body.locale)) issue("locale", `hosted checkout: ${SETTINGS_LOCALES.join(" | ")}`);
320
- const consents = body.consents ?? [];
321
- if (consents.length > 20) issue("consents", `en çok 20 belge`);
322
279
  const seen = /* @__PURE__ */ new Set();
323
- consents.forEach((consent, i) => {
324
- if (consent.url === void 0) ctx.addIssue({
325
- code: "custom",
326
- path: [
327
- "consents",
328
- i,
329
- "url"
330
- ],
331
- message: "hosted checkout'ta zorunlu"
332
- });
333
- if (consent.acceptedAt !== void 0) ctx.addIssue({
334
- code: "custom",
335
- path: [
336
- "consents",
337
- i,
338
- "acceptedAt"
339
- ],
340
- message: "hosted checkout'ta onayı sayfa kaydeder"
341
- });
280
+ body.consents?.forEach((consent, i) => {
342
281
  if (seen.has(consent.document)) ctx.addIssue({
343
282
  code: "custom",
344
283
  path: [
@@ -346,18 +285,11 @@ const CheckoutSessionCreateInputSchema = z.object({
346
285
  i,
347
286
  "document"
348
287
  ],
349
- message: "belge adı tekrar ediyor"
288
+ message: "duplicate document"
350
289
  });
351
290
  seen.add(consent.document);
352
291
  });
353
- }).transform(({ returnUrl, successUrl, ...rest }) => ({
354
- ...rest,
355
- successUrl: successUrl ?? returnUrl
356
- }));
357
- /** Doğrulanmış gövde eski akışı mı istiyor (K16). Hosted: false. */
358
- function isLegacyCheckoutInput(input) {
359
- return input.profile !== void 0 && input.identityNumber !== void 0;
360
- }
292
+ });
361
293
  /**
362
294
  * `canceled` (F2b): müşteri hosted sayfada vazgeçti. Eski (0.2.0 öncesi hosted'sız)
363
295
  * istemci bu değeri tanımaz; yalnızca hosted akışı kullanan ürün görür.
@@ -369,6 +301,7 @@ const CheckoutStatusSchema = z.enum([
369
301
  "expired",
370
302
  "canceled"
371
303
  ]);
304
+ /** Provider form shown on steward's hosted checkout page (never returned to the product). */
372
305
  const ProviderPayloadSchema = z.discriminatedUnion("type", [z.object({
373
306
  type: z.literal("html_snippet"),
374
307
  html: z.string()
@@ -386,12 +319,10 @@ const CheckoutSessionSchema = z.object({
386
319
  currency: CurrencySchema,
387
320
  provider: z.string(),
388
321
  locale: z.string(),
389
- /** Eski ad; `successUrl` ile aynı değer. */
390
- returnUrl: z.string(),
391
- successUrl: z.string().optional(),
392
- /** Hosted: "Vazgeç" hedefi (verilmediyse null; sayfa ürün ayarı `appUrl`e döner). */
393
- cancelUrl: z.string().nullable().optional(),
394
- /** Kabul edilen belgeler (hosted: sayfada onaylandıktan sonra dolar). */
322
+ successUrl: z.string(),
323
+ /** "Cancel" target (null if not given; the page falls back to product setting `appUrl`). */
324
+ cancelUrl: z.string().nullable(),
325
+ /** Accepted documents (filled once accepted on the page). */
395
326
  consents: z.array(ConsentSchema),
396
327
  actorRef: ActorRefSchema.nullable(),
397
328
  failureReason: z.string().nullable(),
@@ -401,15 +332,12 @@ const CheckoutSessionSchema = z.object({
401
332
  completedAt: DateTimeSchema.nullable()
402
333
  });
403
334
  /**
404
- * `POST /v1/accounts/{ref}/checkout-sessions` yanıtı.
405
- * - hosted: `url` = steward checkout sayfası (`…/public/v1/checkout/{id}?k=<anahtar>`;
406
- * anahtar yalnızca burada), `providerPayload` yok, `account` işlem sonrası durum.
407
- * - eski gövde: `url: null`, `providerPayload` sağlayıcı formu.
408
- * `url` F2b öncesi steward'da yoktur (null okunur).
335
+ * `POST /v1/accounts/{ref}/checkout-sessions` response: `url` is steward's checkout page
336
+ * (`…/public/v1/checkout/{id}?k=<key>`; the key appears only here), `account` is the state
337
+ * after the call.
409
338
  */
410
339
  const CheckoutSessionCreatedSchema = CheckoutSessionSchema.extend({
411
- url: z.string().nullable().default(null),
412
- providerPayload: ProviderPayloadSchema.optional(),
340
+ url: z.string(),
413
341
  account: AccountStateSchema.optional()
414
342
  });
415
343
  const RETURN_PLACEHOLDER_RE = /\{(CHECKOUT_ID|STATUS|ACCOUNT_REF|LOCALE)\}/g;
@@ -421,28 +349,22 @@ function originOf(value) {
421
349
  }
422
350
  }
423
351
  /**
424
- * Checkout sonucunun tarayıcı yönlendirmesi — servis ve sahte steward aynı fonksiyonu
425
- * kullanır. Eski oturum: `returnUrl?session=&status=`. Hosted: `successUrl`de yer tutucu
426
- * (`{CHECKOUT_ID}` `{STATUS}` `{ACCOUNT_REF}` `{LOCALE}`) varsa YALNIZCA onlar doldurulur
427
- * (değerler URL-kodlu), parametre eklenmez; yoksa eski biçim. Kodlu değer origin'i
428
- * değiştiremez; yine de denetlenir, değişirse eski biçime düşülür.
352
+ * Browser redirect for a checkout outcome — the service and the fake steward share it.
353
+ * Placeholders in `successUrl` (`{CHECKOUT_ID}` `{STATUS}` `{ACCOUNT_REF}` `{LOCALE}`) are
354
+ * filled with URL-encoded values; nothing is appended, so a URL without placeholders is used
355
+ * as-is. An encoded value cannot change the origin; it is still checked, and if the origin
356
+ * would change the URL is returned unfilled.
429
357
  */
430
358
  function checkoutRedirectUrl(target, status) {
431
359
  const template = target.successUrl;
432
- if (template !== null && template.match(RETURN_PLACEHOLDER_RE)) {
433
- const values = {
434
- CHECKOUT_ID: target.id,
435
- STATUS: status,
436
- ACCOUNT_REF: target.accountRef,
437
- LOCALE: target.locale
438
- };
439
- const filled = template.replace(RETURN_PLACEHOLDER_RE, (_, name) => encodeURIComponent(values[name]));
440
- if (originOf(filled) !== null && originOf(filled) === originOf(template)) return filled;
441
- }
442
- const url = new URL(template ?? target.returnUrl);
443
- url.searchParams.set("session", target.id);
444
- url.searchParams.set("status", status);
445
- return url.toString();
360
+ const values = {
361
+ CHECKOUT_ID: target.id,
362
+ STATUS: status,
363
+ ACCOUNT_REF: target.accountRef,
364
+ LOCALE: target.locale
365
+ };
366
+ const filled = template.replace(RETURN_PLACEHOLDER_RE, (_, name) => encodeURIComponent(values[name]));
367
+ return originOf(filled) !== null && originOf(filled) === originOf(template) ? filled : template;
446
368
  }
447
369
  const CHECKOUT_ERRORS = [
448
370
  "account_not_found",
@@ -708,8 +630,6 @@ const AccountDetailSchema = AccountSchema.extend({
708
630
  const withAccount = { account: AccountStateSchema.optional() };
709
631
  /** `PUT /v1/accounts/{ref}` (201 ya da 200). */
710
632
  const AccountUpsertResponseSchema = AccountSchema.extend(withAccount);
711
- /** `PUT /v1/accounts/{ref}/billing-profile` */
712
- const BillingProfileResponseSchema = BillingProfileSchema.extend(withAccount);
713
633
  /** `POST /v1/accounts/{ref}/grants` */
714
634
  const GrantCreateResponseSchema = GrantSchema.extend(withAccount);
715
635
  /** `POST /v1/subscriptions/{id}/cancel` */
@@ -722,27 +642,6 @@ const ManualInvoiceResponseSchema = InvoiceSchema.extend(withAccount);
722
642
  const InvoiceVoidResponseSchema = InvoiceSchema.extend(withAccount);
723
643
  /** `GET /v1/checkout-sessions/{id}` — `account` yalnızca oturum `completed` ise. */
724
644
  const CheckoutSessionResponseSchema = CheckoutSessionSchema.extend(withAccount);
725
- const DeliverySchema = z.object({
726
- endpointId: IdSchema,
727
- status: z.enum([
728
- "pending",
729
- "delivered",
730
- "dead"
731
- ]),
732
- attempts: z.number().int().nonnegative(),
733
- lastHttpStatus: z.number().int().nullable(),
734
- lastError: z.string().nullable(),
735
- nextAttemptAt: DateTimeSchema.nullable(),
736
- deliveredAt: DateTimeSchema.nullable()
737
- });
738
- const EventRecordSchema = z.object({
739
- event: BillingEventSchema,
740
- deliveries: z.array(DeliverySchema)
741
- });
742
- const EventListSchema = z.object({
743
- items: z.array(EventRecordSchema),
744
- nextCursor: z.string().nullable()
745
- });
746
645
  /** Değişiklik yapan her istekte zorunlu başlıklar. */
747
646
  const HEADER_IDEMPOTENCY_KEY = "idempotency-key";
748
647
  const HEADER_ACTOR_REF = "x-actor-ref";
@@ -761,8 +660,6 @@ const ERROR_CODES = Object.freeze({
761
660
  invalid_request: spec(400, "validation"),
762
661
  invalid_account_ref: spec(400, "validation"),
763
662
  invalid_include: spec(400, "validation"),
764
- invalid_cursor: spec(400, "validation"),
765
- invalid_limit: spec(400, "validation"),
766
663
  invalid_actor_ref: spec(400, "validation"),
767
664
  actor_ref_required: spec(400, "validation"),
768
665
  idempotency_key_required: spec(400, "validation"),
@@ -782,7 +679,6 @@ const ERROR_CODES = Object.freeze({
782
679
  grant_not_found: spec(404, "not_found"),
783
680
  invoice_not_found: spec(404, "not_found"),
784
681
  document_not_found: spec(404, "not_found"),
785
- event_not_found: spec(404, "not_found"),
786
682
  checkout_session_not_found: spec(404, "not_found"),
787
683
  /** Yönetim ucu: endpoint yok ya da başka ürünün. */
788
684
  webhook_endpoint_not_found: spec(404, "not_found"),
@@ -808,7 +704,7 @@ const ERROR_CODES = Object.freeze({
808
704
  subscription_active: spec(409, "conflict"),
809
705
  /** `DELETE /v1/accounts/:ref`: sağlayıcı formu açık checkout oturumu var; süresi dolup kapanınca tekrar dene. */
810
706
  checkout_in_progress: spec(409, "conflict", true),
811
- /** `returnUrl` ürünün izinli origin'lerinde değil: ürün yapılandırması hatalı. */
707
+ /** `successUrl`, `cancelUrl` or portal `returnUrl` is not in the product's allowed origins: product misconfiguration. */
812
708
  return_url_not_allowed: spec(422, "config"),
813
709
  /** Bu deployment'ta sağlayıcı yapılandırılmamış (503 ama beklemekle düzelmez). */
814
710
  provider_not_configured: spec(503, "config"),
@@ -1075,6 +971,17 @@ const DoctorEndpointSchema = z.object({
1075
971
  lastDelivery: WebhookLastDeliverySchema.nullable(),
1076
972
  diagnosis: z.string()
1077
973
  });
974
+ /** Billing emails (issue #15). */
975
+ const DoctorEmailsSchema = z.object({
976
+ /** The deployment has a mail transport (`STEWARD_SMTP_URL`). */
977
+ transportConfigured: z.boolean(),
978
+ /** A sender address is known (`settings.emails.from.address` or the deployment default). */
979
+ senderConfigured: z.boolean(),
980
+ /** Templates the product turned off. */
981
+ disabledTemplates: z.array(z.string()),
982
+ /** Emails are sent: transport and sender configured, at least one template on. */
983
+ ready: z.boolean()
984
+ });
1078
985
  const DoctorReportSchema = z.object({
1079
986
  /** `error` önemde uyarı yok. */
1080
987
  ok: z.boolean(),
@@ -1092,8 +999,22 @@ const DoctorReportSchema = z.object({
1092
999
  dunning: z.object({
1093
1000
  /** true: dunning adımları yalnızca iz yazar (askı/sonlandırma yok). */
1094
1001
  dryRun: z.boolean() }),
1002
+ /** Absent in reports of steward versions without billing emails. */
1003
+ emails: DoctorEmailsSchema.optional(),
1095
1004
  warnings: z.array(DoctorWarningSchema)
1096
1005
  });
1006
+ /** Email readiness from settings and the deployment's transport (pure). */
1007
+ function emailReadinessOf(settings, transport) {
1008
+ const templates = settings.emails?.templates ?? {};
1009
+ const disabledTemplates = EMAIL_TEMPLATES.filter((t) => templates[t]?.enabled === false);
1010
+ const senderConfigured = settings.emails?.from?.address !== void 0 || transport.defaultFromConfigured;
1011
+ return {
1012
+ transportConfigured: transport.configured,
1013
+ senderConfigured,
1014
+ disabledTemplates: [...disabledTemplates],
1015
+ ready: transport.configured && senderConfigured && disabledTemplates.length < EMAIL_TEMPLATES.length
1016
+ };
1017
+ }
1097
1018
  /** Bekleyen en eski teslimat bundan eskiyse `endpoint_pending_backlog` (K23 alarmıyla aynı eşik). */
1098
1019
  const DOCTOR_PENDING_BACKLOG_MS = 36e5;
1099
1020
  /** Son denenmiş teslimattan endpoint teşhisi (saf). */
@@ -1181,6 +1102,12 @@ function buildDoctorReport(facts) {
1181
1102
  if (endpoint.dead > 0) warn("warning", "endpoint_dead_deliveries", `${endpoint.dead} teslimattan vazgeçildi (dead): etkilenen hesaplar için resync`, endpoint.id);
1182
1103
  if (endpoint.oldestPendingAt !== null && facts.checkedAt.getTime() - Date.parse(endpoint.oldestPendingAt) > 36e5) warn("warning", "endpoint_pending_backlog", `${endpoint.pending} teslimat bekliyor; en eskisi bir saatten eski`, endpoint.id);
1183
1104
  }
1105
+ const emails = facts.emailTransport ? emailReadinessOf(settings, facts.emailTransport) : void 0;
1106
+ if (emails && emails.disabledTemplates.length < EMAIL_TEMPLATES.length) {
1107
+ if (!emails.transportConfigured) warn("warning", "email_not_configured", "email is not configured: steward sends no billing emails (STEWARD_SMTP_URL)");
1108
+ else if (!emails.senderConfigured) warn("warning", "email_sender_missing", "no email sender: set `settings.emails.from.address` or STEWARD_EMAIL_FROM");
1109
+ if (settings.emails?.billingPath === void 0) warn("info", "email_billing_link_missing", "`settings.emails.billingPath` is not set: email buttons open `appUrl`");
1110
+ }
1184
1111
  if (dunning.dryRun) warn("info", "dunning_dry_run", "dunning dry-run: adımlar yalnızca iz yazar, askı ve sonlandırma uygulanmaz");
1185
1112
  return {
1186
1113
  ok: !warnings.some((w) => w.severity === "error"),
@@ -1195,8 +1122,9 @@ function buildDoctorReport(facts) {
1195
1122
  hosted,
1196
1123
  endpoints,
1197
1124
  dunning,
1125
+ ...emails ? { emails } : {},
1198
1126
  warnings
1199
1127
  };
1200
1128
  }
1201
1129
  //#endregion
1202
- export { HEADER_SDK as $, FeatureTypeSchema as $t, webhookEndpointUrlProblem as A, MAX_CHECKOUT_CONSENT_DOCS as At, AccountDetailSchema as B, ReturnOriginSchema as Bt, WebhookEndpointListSchema as C, CheckoutConsentInputSchema as Ct, WebhookLastDeliverySchema as D, CheckoutSessionSchema as Dt, WebhookEndpointStatusSchema as E, CheckoutSessionCreatedSchema as Et, PlanSetWarningSchema as F, BrandingSchema as Ft, DeliverySchema as G, applySettingsPatch as Gt, BillingProfileResponseSchema as H, SettingsLocaleSchema as Ht, ResyncResultSchema as I, DEFAULT_CHECKOUT_TTL_MINUTES as It, GrantCreateResponseSchema as J, AccountsRefreshResultSchema as Jt, EventListSchema as K, isReturnUrlAllowed as Kt, ERROR_CODES as L, DEFAULT_PORTAL_TTL_MINUTES as Lt, PlanSetInputSchema as M, checkoutRedirectUrl as Mt, PlanSetModeSchema as N, isLegacyCheckoutInput as Nt, WebhookSecretSchema as O, CheckoutStatusSchema as Ot, PlanSetResponseSchema as P, BrandingPatchSchema as Pt, HEADER_REQUEST_ID as Q, FeatureDefSchema as Qt, errorCodeSpec as R, ProductSettingsPatchSchema as Rt, WebhookEndpointCreateInputSchema as S, CHECKOUT_ERRORS as St, WebhookEndpointSchema as T, CheckoutSessionCreateInputSchema as Tt, CancelSubscriptionResponseSchema as U, TtlMinutesSchema as Ut, AccountUpsertResponseSchema as V, SETTINGS_LOCALES as Vt, CheckoutSessionResponseSchema as W, WebUrlSchema as Wt, HEADER_API_VERSION as X, CatalogSyncInputSchema as Xt, HEADER_ACTOR_REF as Y, CatalogSchema as Yt, HEADER_IDEMPOTENCY_KEY as Z, CatalogSyncResultSchema as Zt, hostedReadinessOf as _, StatsSchema as _t, DOCTOR_SEVERITIES as a, InvoiceSchema as at, WEBHOOK_ENDPOINT_STATUSES as b, PortalSessionCreateInputSchema as bt, DoctorHostedSchema as c, MANUAL_INVOICE_MAX_AMOUNT_MINOR as ct, DoctorSettingsSchema as d, manualInvoiceTotals as dt, PlanDefSchema as en, InvoiceIssueResponseSchema as et, DoctorSeveritySchema as f, splitInclusiveTax as ft, endpointDiagnosisOf as g, PaymentSchema as gt, buildDoctorReport as h, GrantSchema as ht, DOCTOR_REQUIRED_LEGAL_LINKS as i, InvoiceLineSchema as it, PLAN_SET_MODES as j, ProviderPayloadSchema as jt, generateWebhookSecret as k, ConsentSchema as kt, DoctorProviderSchema as l, ManualInvoiceInputSchema as lt, DoctorWarningSchema as m, GrantCreateInputSchema as mt, DOCTOR_PROVIDER_ENVIRONMENTS as n, PriceSchema as nn, ManualInvoiceResponseSchema as nt, DoctorCatalogSchema as o, InvoiceStatusSchema as ot, DoctorUnsellablePriceSchema as p, CancelSubscriptionInputSchema as pt, EventRecordSchema as q, returnOriginsOf as qt, DOCTOR_REQUIRED_BRANDING as r, InvoiceIssueInputSchema as rt, DoctorEndpointSchema as s, InvoiceVoidInputSchema as st, DOCTOR_PENDING_BACKLOG_MS as t, PriceDefSchema as tn, InvoiceVoidResponseSchema as tt, DoctorReportSchema as u, ManualInvoiceLineInputSchema as ut, DEFAULT_ENDPOINT_EVENT_TYPES as v, SubscriptionSchema as vt, WebhookEndpointRotateInputSchema as w, CheckoutPrefillSchema as wt, WEBHOOK_SECRET_PREFIX as x, PortalSessionSchema as xt, EventTypeSchema as y, PORTAL_SESSION_ERRORS as yt, isKnownErrorCode as z, ProductSettingsSchema as zt };
1130
+ export { InvoiceVoidResponseSchema as $, PriceSchema as $t, WebhookSecretSchema as A, checkoutRedirectUrl as At, errorCodeSpec as B, TtlMinutesSchema as Bt, WEBHOOK_SECRET_PREFIX as C, CheckoutSessionCreateInputSchema as Ct, WebhookEndpointSchema as D, ConsentSchema as Dt, WebhookEndpointRotateInputSchema as E, CheckoutStatusSchema as Et, PlanSetModeSchema as F, ProductSettingsPatchSchema as Ft, CheckoutSessionResponseSchema as G, AccountsRefreshResultSchema as Gt, AccountDetailSchema as H, applySettingsPatch as Ht, PlanSetResponseSchema as I, ProductSettingsSchema as It, HEADER_API_VERSION as J, CatalogSyncResultSchema as Jt, GrantCreateResponseSchema as K, CatalogSchema as Kt, PlanSetWarningSchema as L, ReturnOriginSchema as Lt, webhookEndpointUrlProblem as M, BrandingSchema as Mt, PLAN_SET_MODES as N, DEFAULT_CHECKOUT_TTL_MINUTES as Nt, WebhookEndpointStatusSchema as O, MAX_CHECKOUT_CONSENT_DOCS as Ot, PlanSetInputSchema as P, DEFAULT_PORTAL_TTL_MINUTES as Pt, InvoiceIssueResponseSchema as Q, PriceDefSchema as Qt, ResyncResultSchema as R, SETTINGS_LOCALES as Rt, WEBHOOK_ENDPOINT_STATUSES as S, CheckoutPrefillSchema as St, WebhookEndpointListSchema as T, CheckoutSessionSchema as Tt, AccountUpsertResponseSchema as U, isReturnUrlAllowed as Ut, isKnownErrorCode as V, WebUrlSchema as Vt, CancelSubscriptionResponseSchema as W, returnOriginsOf as Wt, HEADER_REQUEST_ID as X, FeatureTypeSchema as Xt, HEADER_IDEMPOTENCY_KEY as Y, FeatureDefSchema as Yt, HEADER_SDK as Z, PlanDefSchema as Zt, emailReadinessOf as _, PORTAL_SESSION_ERRORS as _t, DOCTOR_SEVERITIES as a, InvoiceVoidInputSchema as at, DEFAULT_ENDPOINT_EVENT_TYPES as b, CHECKOUT_ERRORS as bt, DoctorEndpointSchema as c, ManualInvoiceLineInputSchema as ct, DoctorReportSchema as d, CancelSubscriptionInputSchema as dt, ManualInvoiceResponseSchema as et, DoctorSettingsSchema as f, GrantCreateInputSchema as ft, buildDoctorReport as g, SubscriptionSchema as gt, DoctorWarningSchema as h, StatsSchema as ht, DOCTOR_REQUIRED_LEGAL_LINKS as i, InvoiceStatusSchema as it, generateWebhookSecret as j, BrandingPatchSchema as jt, WebhookLastDeliverySchema as k, ProviderPayloadSchema as kt, DoctorHostedSchema as l, manualInvoiceTotals as lt, DoctorUnsellablePriceSchema as m, PaymentSchema as mt, DOCTOR_PROVIDER_ENVIRONMENTS as n, InvoiceLineSchema as nt, DoctorCatalogSchema as o, MANUAL_INVOICE_MAX_AMOUNT_MINOR as ot, DoctorSeveritySchema as p, GrantSchema as pt, HEADER_ACTOR_REF as q, CatalogSyncInputSchema as qt, DOCTOR_REQUIRED_BRANDING as r, InvoiceSchema as rt, DoctorEmailsSchema as s, ManualInvoiceInputSchema as st, DOCTOR_PENDING_BACKLOG_MS as t, InvoiceIssueInputSchema as tt, DoctorProviderSchema as u, splitInclusiveTax as ut, endpointDiagnosisOf as v, PortalSessionCreateInputSchema as vt, WebhookEndpointCreateInputSchema as w, CheckoutSessionCreatedSchema as wt, EventTypeSchema as x, CheckoutConsentInputSchema as xt, hostedReadinessOf as y, PortalSessionSchema as yt, ERROR_CODES as z, SettingsLocaleSchema as zt };