@stewardhq/sdk 0.3.0 → 0.5.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.
- package/README.md +122 -92
- package/dist/_chunks/errors.js +1 -1
- package/dist/_chunks/events.d.ts +301 -4
- package/dist/_chunks/events.js +995 -17
- package/dist/_chunks/index.d.ts +3129 -63
- package/dist/_chunks/locale.d.ts +269 -1
- package/dist/_chunks/src.js +807 -25
- package/dist/_chunks/validators.d.ts +45 -1
- package/dist/_chunks/validators.js +128 -27
- package/dist/contract.d.ts +4 -4
- package/dist/contract.js +4 -4
- package/dist/index.d.ts +436 -22
- package/dist/index.js +1659 -26
- package/dist/server.d.ts +44 -6
- package/dist/server.js +29 -2
- package/package.json +2 -7
- package/dist/_chunks/steward.d.ts +0 -189
- package/dist/_chunks/steward.js +0 -564
- package/dist/testing/fixtures/events/LOCK.json +0 -27
- package/dist/testing/fixtures/events/account.deleted.json +0 -36
- package/dist/testing/fixtures/events/account.state_changed.json +0 -53
- package/dist/testing/fixtures/events/account.updated.json +0 -37
- package/dist/testing/fixtures/events/checkout.completed.json +0 -36
- package/dist/testing/fixtures/events/checkout.expired.json +0 -26
- package/dist/testing/fixtures/events/checkout.failed.json +0 -27
- package/dist/testing/fixtures/events/invoice.created.json +0 -37
- package/dist/testing/fixtures/events/invoice.issued.json +0 -38
- package/dist/testing/fixtures/events/invoice.voided.json +0 -39
- package/dist/testing/fixtures/events/subscription.activated.json +0 -40
- package/dist/testing/fixtures/events/subscription.cancel_scheduled.json +0 -38
- package/dist/testing/fixtures/events/subscription.canceled.json +0 -29
- package/dist/testing/fixtures/events/subscription.expired.json +0 -27
- package/dist/testing/fixtures/events/subscription.payment_failed.json +0 -38
- package/dist/testing/fixtures/events/subscription.reactivated.json +0 -36
- package/dist/testing/fixtures/events/subscription.renewed.json +0 -39
- package/dist/testing/fixtures/events/subscription.suspended.json +0 -36
- package/dist/testing/fixtures/events/subscription.terminated.json +0 -28
- package/dist/testing.d.ts +0 -656
- package/dist/testing.js +0 -3684
package/dist/_chunks/steward.js
DELETED
|
@@ -1,564 +0,0 @@
|
|
|
1
|
-
import { X as ActorRefSchema, Y as API_VERSION, at as ErrorResponseSchema, b as AccountStateSchema, p as AccountDeleteResponseSchema } from "./events.js";
|
|
2
|
-
import { $ as InvoiceVoidResponseSchema, B as errorCodeSpec, D as WebhookEndpointSchema, G as CheckoutSessionResponseSchema, Gt as AccountsRefreshResultSchema, H as AccountDetailSchema, I as PlanSetResponseSchema, It as ProductSettingsSchema, J as HEADER_API_VERSION, Jt as CatalogSyncResultSchema, K as GrantCreateResponseSchema, Kt as CatalogSchema, Q as InvoiceIssueResponseSchema, R as ResyncResultSchema, T as WebhookEndpointListSchema, U as AccountUpsertResponseSchema, W as CancelSubscriptionResponseSchema, X as HEADER_REQUEST_ID, Y as HEADER_IDEMPOTENCY_KEY, Z as HEADER_SDK, d as DoctorReportSchema, et as ManualInvoiceResponseSchema, ht as StatsSchema, q as HEADER_ACTOR_REF, wt as CheckoutSessionCreatedSchema, yt as PortalSessionSchema } from "./src.js";
|
|
3
|
-
import { a as StewardNetworkError, n as StewardConfigError, r as StewardContractError, t as StewardApiError } from "./errors.js";
|
|
4
|
-
import { z } from "zod";
|
|
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
|
-
const ref = (value) => encodeURIComponent(value);
|
|
125
|
-
return {
|
|
126
|
-
getCatalog: () => call(CatalogSchema, "GET", "/v1/catalog"),
|
|
127
|
-
syncCatalog: (input, opts) => call(CatalogSyncResultSchema, "PUT", "/v1/admin/catalog", input, opts),
|
|
128
|
-
upsertAccount: (accountRef, input, opts) => call(AccountUpsertResponseSchema, "PUT", `/v1/accounts/${ref(accountRef)}`, input, opts),
|
|
129
|
-
getAccount: (accountRef, include = []) => call(AccountDetailSchema, "GET", `/v1/accounts/${ref(accountRef)}${include.length ? `?include=${include.join(",")}` : ""}`),
|
|
130
|
-
/**
|
|
131
|
-
* Hesap durumu (`AccountState`). ETag = `"<version>"`; önceki yanıtın
|
|
132
|
-
* `etag`'i verilirse ve durum değişmediyse 304 → `{notModified: true}`.
|
|
133
|
-
*/
|
|
134
|
-
getAccountState: async (accountRef, opts = {}) => {
|
|
135
|
-
const sent = await send("GET", `/v1/accounts/${ref(accountRef)}/state`, void 0, void 0, opts.etag !== void 0 ? { "if-none-match": opts.etag } : void 0);
|
|
136
|
-
if (!("res" in sent)) return sent;
|
|
137
|
-
const { res } = sent;
|
|
138
|
-
const etag = res.headers.get("etag");
|
|
139
|
-
if (res.status === 304) {
|
|
140
|
-
const current = etag ?? opts.etag;
|
|
141
|
-
return current === void 0 ? invalid(res) : success(res, {
|
|
142
|
-
notModified: true,
|
|
143
|
-
etag: current
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
if (!res.ok) return failure(res);
|
|
147
|
-
const parsed = AccountStateSchema.safeParse(await res.json().catch(() => void 0));
|
|
148
|
-
if (!parsed.success) return invalid(res);
|
|
149
|
-
return success(res, {
|
|
150
|
-
notModified: false,
|
|
151
|
-
etag,
|
|
152
|
-
state: parsed.data
|
|
153
|
-
});
|
|
154
|
-
},
|
|
155
|
-
/** Atomik plan (K6): aktif plan grant'leri → tek plan grant'i (`planCode: null` kaldırır); `strict`'te 409 `subscription_plan_higher`. */
|
|
156
|
-
setPlan: (accountRef, input, opts) => call(PlanSetResponseSchema, "PUT", `/v1/accounts/${ref(accountRef)}/plan`, input, opts),
|
|
157
|
-
/** Saklı durumu `account.state_changed` (`cause: "resync"`) olarak yeniden yayınlar; version değişmez (202). */
|
|
158
|
-
resync: (accountRef, opts) => call(ResyncResultSchema, "POST", `/v1/accounts/${ref(accountRef)}/resync`, void 0, opts),
|
|
159
|
-
/** KVKK anonimleştirme (F4e): `{ref, deletedAt}`; silinmişse 410 `account_deleted`, canlı abonelikte 409 `subscription_active`. */
|
|
160
|
-
deleteAccount: (accountRef, opts) => call(AccountDeleteResponseSchema, "DELETE", `/v1/accounts/${ref(accountRef)}`, void 0, opts),
|
|
161
|
-
createCheckoutSession: (accountRef, input, opts) => call(CheckoutSessionCreatedSchema, "POST", `/v1/accounts/${ref(accountRef)}/checkout-sessions`, input, opts),
|
|
162
|
-
getCheckoutSession: (id) => call(CheckoutSessionResponseSchema, "GET", `/v1/checkout-sessions/${ref(id)}`),
|
|
163
|
-
createPortalSession: (accountRef, input = {}, opts) => call(PortalSessionSchema, "POST", `/v1/accounts/${ref(accountRef)}/portal-sessions`, input, opts),
|
|
164
|
-
cancelSubscription: (id, input, opts) => call(CancelSubscriptionResponseSchema, "POST", `/v1/subscriptions/${ref(id)}/cancel`, input, opts),
|
|
165
|
-
createGrant: (accountRef, input, opts) => call(GrantCreateResponseSchema, "POST", `/v1/accounts/${ref(accountRef)}/grants`, input, opts),
|
|
166
|
-
revokeGrant: (id, opts) => empty("DELETE", `/v1/grants/${ref(id)}`, opts),
|
|
167
|
-
getStats: () => call(StatsSchema, "GET", "/v1/stats"),
|
|
168
|
-
doctor: () => call(DoctorReportSchema, "GET", "/v1/doctor"),
|
|
169
|
-
issueInvoice: (id, input, opts) => call(InvoiceIssueResponseSchema, "POST", `/v1/invoices/${ref(id)}/issue`, input, opts),
|
|
170
|
-
/** Manuel taslak fatura (F4f): profil gerekir (422 `billing_profile_required`); `issueInvoice` ile kesilir. */
|
|
171
|
-
createManualInvoice: (accountRef, input, opts) => call(ManualInvoiceResponseSchema, "POST", `/v1/accounts/${ref(accountRef)}/invoices`, input, opts),
|
|
172
|
-
/** Yalnızca manuel fatura (409 `invoice_has_payment` / `invoice_not_voidable`); `invoice.voided`. */
|
|
173
|
-
voidInvoice: (id, input, opts) => call(InvoiceVoidResponseSchema, "POST", `/v1/invoices/${ref(id)}/void`, input, opts),
|
|
174
|
-
getInvoiceDocument: async (id) => {
|
|
175
|
-
const sent = await send("GET", `/v1/invoices/${ref(id)}/document`, void 0, void 0);
|
|
176
|
-
if (!("res" in sent)) return sent;
|
|
177
|
-
const { res } = sent;
|
|
178
|
-
if (!res.ok) return failure(res);
|
|
179
|
-
return success(res, {
|
|
180
|
-
contentType: res.headers.get("content-type") ?? "application/octet-stream",
|
|
181
|
-
bytes: new Uint8Array(await res.arrayBuffer())
|
|
182
|
-
});
|
|
183
|
-
},
|
|
184
|
-
getSettings: () => call(ProductSettingsSchema, "GET", "/v1/admin/settings"),
|
|
185
|
-
updateSettings: (patch, opts) => call(ProductSettingsSchema, "PATCH", "/v1/admin/settings", patch, opts),
|
|
186
|
-
listWebhookEndpoints: () => call(WebhookEndpointListSchema, "GET", "/v1/admin/webhook-endpoints"),
|
|
187
|
-
createWebhookEndpoint: (input, opts) => call(WebhookEndpointSchema, "POST", "/v1/admin/webhook-endpoints", input, opts),
|
|
188
|
-
rotateWebhookEndpointSecret: (id, input, opts) => call(WebhookEndpointSchema, "POST", `/v1/admin/webhook-endpoints/${ref(id)}/rotate`, input, opts),
|
|
189
|
-
deactivateWebhookEndpoint: (id, opts) => call(WebhookEndpointSchema, "POST", `/v1/admin/webhook-endpoints/${ref(id)}/deactivate`, void 0, opts)
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
//#endregion
|
|
193
|
-
//#region src/retry.ts
|
|
194
|
-
const RETRY_CLASSES = [
|
|
195
|
-
"network",
|
|
196
|
-
"5xx",
|
|
197
|
-
"429"
|
|
198
|
-
];
|
|
199
|
-
const BASE_DELAY_MS = 250;
|
|
200
|
-
function retryPolicy(options = {}) {
|
|
201
|
-
const attempts = options.attempts ?? 2;
|
|
202
|
-
const maxDelayMs = options.maxDelayMs ?? 5e3;
|
|
203
|
-
const on = options.on ?? RETRY_CLASSES;
|
|
204
|
-
const problems = [];
|
|
205
|
-
if (!Number.isInteger(attempts) || attempts < 0) problems.push(`retries.attempts negatif olmayan tam sayı olmalı: ${attempts}`);
|
|
206
|
-
if (!Number.isFinite(maxDelayMs) || maxDelayMs < 0) problems.push(`retries.maxDelayMs negatif olmayan sayı olmalı: ${maxDelayMs}`);
|
|
207
|
-
for (const value of on) if (!RETRY_CLASSES.includes(value)) problems.push(`retries.on bilinmeyen sınıf: ${String(value)} (${RETRY_CLASSES.join(", ")})`);
|
|
208
|
-
if (problems.length > 0) throw new StewardConfigError({ message: `createSteward: ${problems.join("; ")}` });
|
|
209
|
-
return {
|
|
210
|
-
attempts,
|
|
211
|
-
on: new Set(on),
|
|
212
|
-
maxDelayMs,
|
|
213
|
-
sleep: options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
214
|
-
random: options.random ?? Math.random
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
/** Başarısız sonucun tekrar sınıfı; hiçbirine girmiyorsa null (4xx, sözleşme hatası). */
|
|
218
|
-
function retryClassOf(failure) {
|
|
219
|
-
if (failure.status === 0) return "network";
|
|
220
|
-
if (failure.status === 429) return "429";
|
|
221
|
-
if (failure.status >= 500) return "5xx";
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
function shouldRetry(request, failure, policy) {
|
|
225
|
-
if (request.method !== "GET" && request.idempotencyKey === void 0) return false;
|
|
226
|
-
const kind = retryClassOf(failure);
|
|
227
|
-
if (kind === null || !policy.on.has(kind)) return false;
|
|
228
|
-
if (kind === "network") return true;
|
|
229
|
-
return (failure.retryable ?? errorCodeSpec(failure.error)?.retryable) !== false;
|
|
230
|
-
}
|
|
231
|
-
/** `retry`. tekrardan (1'den başlar) önceki bekleme, ms. */
|
|
232
|
-
function retryDelayMs(retry, failure, policy) {
|
|
233
|
-
if (failure.retryAfterMs !== void 0) return Math.min(failure.retryAfterMs, policy.maxDelayMs);
|
|
234
|
-
const ceiling = Math.min(policy.maxDelayMs, BASE_DELAY_MS * 2 ** (retry - 1));
|
|
235
|
-
return Math.round(ceiling / 2 + policy.random() * ceiling / 2);
|
|
236
|
-
}
|
|
237
|
-
/** `send`'i politika izin verdikçe tekrarlar; son sonucu (başarılı ya da değil) döner. */
|
|
238
|
-
async function withRetries(request, policy, send) {
|
|
239
|
-
let result = await send();
|
|
240
|
-
for (let retry = 1; retry <= policy.attempts && !result.ok && shouldRetry(request, result, policy); retry++) {
|
|
241
|
-
await policy.sleep(retryDelayMs(retry, result, policy));
|
|
242
|
-
result = await send();
|
|
243
|
-
}
|
|
244
|
-
return result;
|
|
245
|
-
}
|
|
246
|
-
//#endregion
|
|
247
|
-
//#region src/steward.ts
|
|
248
|
-
const SDK_HEADER = `@stewardhq/sdk/0.3.0`;
|
|
249
|
-
const MeSchema = z.object({
|
|
250
|
-
product: z.string(),
|
|
251
|
-
keyName: z.string(),
|
|
252
|
-
scope: z.enum([
|
|
253
|
-
"product",
|
|
254
|
-
"admin",
|
|
255
|
-
"checkout"
|
|
256
|
-
])
|
|
257
|
-
});
|
|
258
|
-
const defaultEnv = () => globalThis.process?.env ?? {};
|
|
259
|
-
function isHttpUrl(value) {
|
|
260
|
-
try {
|
|
261
|
-
const { protocol } = new URL(value);
|
|
262
|
-
return protocol === "http:" || protocol === "https:";
|
|
263
|
-
} catch {
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
/** Nesne ağacındaki her metodu, istek göndermeden `error(yol)` ile reddeden eşiyle değiştirir. */
|
|
268
|
-
function rejectAll(api, error, prefix = "") {
|
|
269
|
-
return Object.fromEntries(Object.entries(api).map(([name, value]) => {
|
|
270
|
-
const path = `${prefix}${name}`;
|
|
271
|
-
if (typeof value === "function") return [name, () => Promise.reject(error(path))];
|
|
272
|
-
return [name, typeof value === "object" && value !== null ? rejectAll(value, error, `${path}.`) : value];
|
|
273
|
-
}));
|
|
274
|
-
}
|
|
275
|
-
function createSteward(options = {}) {
|
|
276
|
-
const env = options.env ?? defaultEnv();
|
|
277
|
-
const setting = (value, name) => {
|
|
278
|
-
const text = (value ?? env[name])?.trim();
|
|
279
|
-
return text === void 0 || text === "" ? void 0 : text;
|
|
280
|
-
};
|
|
281
|
-
const baseUrl = setting(options.baseUrl, "STEWARD_URL");
|
|
282
|
-
const apiKey = setting(options.apiKey, "STEWARD_API_KEY");
|
|
283
|
-
const adminBaseUrl = setting(options.adminBaseUrl, "STEWARD_ADMIN_URL");
|
|
284
|
-
const adminApiKey = setting(options.adminApiKey, "STEWARD_ADMIN_API_KEY");
|
|
285
|
-
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
286
|
-
const adminOnly = baseUrl === void 0 && apiKey === void 0 && adminBaseUrl !== void 0 && adminApiKey !== void 0;
|
|
287
|
-
const problems = [];
|
|
288
|
-
if (!adminOnly) {
|
|
289
|
-
if (baseUrl === void 0) problems.push("STEWARD_URL (baseUrl) tanımlı değil");
|
|
290
|
-
else if (!isHttpUrl(baseUrl)) problems.push("STEWARD_URL (baseUrl) geçerli bir http(s) adresi değil");
|
|
291
|
-
if (apiKey === void 0) problems.push("STEWARD_API_KEY (apiKey) tanımlı değil");
|
|
292
|
-
}
|
|
293
|
-
if (adminBaseUrl !== void 0 && !isHttpUrl(adminBaseUrl)) problems.push("STEWARD_ADMIN_URL (adminBaseUrl) geçerli bir http(s) adresi değil");
|
|
294
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) problems.push(`timeoutMs pozitif sayı olmalı: ${timeoutMs}`);
|
|
295
|
-
if (options.actorRef !== void 0 && !ActorRefSchema.safeParse(options.actorRef).success) problems.push("actorRef biçimi geçersiz (tür[:kimlik], ör. system, user:42)");
|
|
296
|
-
if (problems.length > 0) {
|
|
297
|
-
const hint = baseUrl === void 0 && apiKey === void 0 ? " (yalnızca admin.* için STEWARD_ADMIN_URL + STEWARD_ADMIN_API_KEY yeter)" : "";
|
|
298
|
-
throw new StewardConfigError({ message: `createSteward: ${problems.join("; ")}${hint}` });
|
|
299
|
-
}
|
|
300
|
-
const policy = retryPolicy(options.retries);
|
|
301
|
-
const doFetch = options.fetch ?? fetch;
|
|
302
|
-
const clientOptions = (target) => ({
|
|
303
|
-
...target,
|
|
304
|
-
timeoutMs,
|
|
305
|
-
sdk: SDK_HEADER,
|
|
306
|
-
...options.fetch !== void 0 ? { fetch: options.fetch } : {}
|
|
307
|
-
});
|
|
308
|
-
const adminTarget = adminBaseUrl !== void 0 && adminApiKey !== void 0 ? {
|
|
309
|
-
baseUrl: adminBaseUrl.replace(/\/$/, ""),
|
|
310
|
-
apiKey: adminApiKey
|
|
311
|
-
} : null;
|
|
312
|
-
const adminClient = adminTarget === null ? null : createBillingClient(clientOptions(adminTarget));
|
|
313
|
-
const productTarget = baseUrl !== void 0 && apiKey !== void 0 ? {
|
|
314
|
-
baseUrl: baseUrl.replace(/\/$/, ""),
|
|
315
|
-
apiKey
|
|
316
|
-
} : null;
|
|
317
|
-
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ı` });
|
|
318
|
-
const raw = createBillingClient(clientOptions(productTarget ?? {
|
|
319
|
-
baseUrl: "http://steward.invalid",
|
|
320
|
-
apiKey: "-"
|
|
321
|
-
}));
|
|
322
|
-
const eitherTarget = productTarget ?? adminTarget;
|
|
323
|
-
const eitherClient = productTarget !== null ? raw : adminClient;
|
|
324
|
-
function requireAdmin() {
|
|
325
|
-
if (adminClient !== null && adminTarget !== null) return {
|
|
326
|
-
client: adminClient,
|
|
327
|
-
target: adminTarget
|
|
328
|
-
};
|
|
329
|
-
const missing = [...adminBaseUrl === void 0 ? ["STEWARD_ADMIN_URL (adminBaseUrl)"] : [], ...adminApiKey === void 0 ? ["STEWARD_ADMIN_API_KEY (adminApiKey)"] : []];
|
|
330
|
-
throw new StewardConfigError({ message: `steward.admin: ${missing.join(" ve ")} tanımlı değil` });
|
|
331
|
-
}
|
|
332
|
-
async function send(target, method, path, call, extraHeaders = {}) {
|
|
333
|
-
const headers = {
|
|
334
|
-
authorization: `Bearer ${target.apiKey}`,
|
|
335
|
-
accept: "application/json",
|
|
336
|
-
[HEADER_API_VERSION]: API_VERSION,
|
|
337
|
-
[HEADER_SDK]: SDK_HEADER,
|
|
338
|
-
...extraHeaders
|
|
339
|
-
};
|
|
340
|
-
if (call.idempotencyKey !== void 0) headers[HEADER_IDEMPOTENCY_KEY] = call.idempotencyKey;
|
|
341
|
-
if (call.actorRef !== void 0) headers[HEADER_ACTOR_REF] = call.actorRef;
|
|
342
|
-
try {
|
|
343
|
-
return { res: await doFetch(`${target.baseUrl}${path}`, {
|
|
344
|
-
method,
|
|
345
|
-
headers,
|
|
346
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
347
|
-
}) };
|
|
348
|
-
} catch (error) {
|
|
349
|
-
const name = typeof error === "object" && error !== null ? error.name : void 0;
|
|
350
|
-
return {
|
|
351
|
-
ok: false,
|
|
352
|
-
status: 0,
|
|
353
|
-
error: name === "TimeoutError" || name === "AbortError" ? "timeout" : "network_error"
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
function meta(res) {
|
|
358
|
-
const requestId = res.headers.get(HEADER_REQUEST_ID);
|
|
359
|
-
const retryAfterMs = res.status === 429 || res.status === 503 ? parseRetryAfter(res.headers.get("retry-after")) : null;
|
|
360
|
-
return {
|
|
361
|
-
...requestId ? { requestId } : {},
|
|
362
|
-
...retryAfterMs !== null ? { retryAfterMs } : {}
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
async function failureOf(res) {
|
|
366
|
-
const parsed = ErrorResponseSchema.safeParse(await res.json().catch(() => null));
|
|
367
|
-
const { requestId, retryAfterMs } = meta(res);
|
|
368
|
-
const common = { ...retryAfterMs !== void 0 ? { retryAfterMs } : {} };
|
|
369
|
-
if (!parsed.success) return {
|
|
370
|
-
ok: false,
|
|
371
|
-
status: res.status,
|
|
372
|
-
error: `http_${res.status}`,
|
|
373
|
-
...requestId !== void 0 ? { requestId } : {},
|
|
374
|
-
...common
|
|
375
|
-
};
|
|
376
|
-
const { error, message, category, retryable } = parsed.data;
|
|
377
|
-
const id = requestId ?? parsed.data.requestId;
|
|
378
|
-
return {
|
|
379
|
-
ok: false,
|
|
380
|
-
status: res.status,
|
|
381
|
-
error,
|
|
382
|
-
...message !== void 0 ? { message } : {},
|
|
383
|
-
...category !== void 0 ? { category } : {},
|
|
384
|
-
...retryable !== void 0 ? { retryable } : {},
|
|
385
|
-
...id !== void 0 ? { requestId: id } : {},
|
|
386
|
-
...common
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
function success(res, data) {
|
|
390
|
-
const { requestId } = meta(res);
|
|
391
|
-
return {
|
|
392
|
-
ok: true,
|
|
393
|
-
status: res.status,
|
|
394
|
-
data,
|
|
395
|
-
...requestId !== void 0 ? { requestId } : {}
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
function invalid(res) {
|
|
399
|
-
const { requestId } = meta(res);
|
|
400
|
-
return {
|
|
401
|
-
ok: false,
|
|
402
|
-
status: res.status,
|
|
403
|
-
error: "invalid_response",
|
|
404
|
-
...requestId !== void 0 ? { requestId } : {}
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
async function sendParsed(target, method, path, schema, call) {
|
|
408
|
-
const sent = await send(target, method, path, call);
|
|
409
|
-
if (!("res" in sent)) return sent;
|
|
410
|
-
if (!sent.res.ok) return failureOf(sent.res);
|
|
411
|
-
const parsed = schema.safeParse(await sent.res.json().catch(() => void 0));
|
|
412
|
-
return parsed.success ? success(sent.res, parsed.data) : invalid(sent.res);
|
|
413
|
-
}
|
|
414
|
-
async function readCatalog(etag) {
|
|
415
|
-
const sent = await send(productTarget, "GET", "/v1/catalog", {}, etag !== void 0 ? { "if-none-match": etag } : {});
|
|
416
|
-
if (!("res" in sent)) return sent;
|
|
417
|
-
const { res } = sent;
|
|
418
|
-
const responseEtag = res.headers.get("etag");
|
|
419
|
-
if (res.status === 304) {
|
|
420
|
-
const current = responseEtag ?? etag;
|
|
421
|
-
return current === void 0 ? invalid(res) : success(res, {
|
|
422
|
-
notModified: true,
|
|
423
|
-
etag: current
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
if (!res.ok) return failureOf(res);
|
|
427
|
-
const parsed = CatalogSchema.safeParse(await res.json().catch(() => void 0));
|
|
428
|
-
return parsed.success ? success(res, {
|
|
429
|
-
notModified: false,
|
|
430
|
-
etag: responseEtag,
|
|
431
|
-
catalog: parsed.data
|
|
432
|
-
}) : invalid(res);
|
|
433
|
-
}
|
|
434
|
-
function toError(failure) {
|
|
435
|
-
const context = {
|
|
436
|
-
...failure.requestId !== void 0 ? { requestId: failure.requestId } : {},
|
|
437
|
-
...failure.retryAfterMs !== void 0 ? { retryAfterMs: failure.retryAfterMs } : {}
|
|
438
|
-
};
|
|
439
|
-
if (failure.status === 0) return new StewardNetworkError({
|
|
440
|
-
code: failure.error === "timeout" ? "timeout" : "network_error",
|
|
441
|
-
...context
|
|
442
|
-
});
|
|
443
|
-
if (failure.error === "invalid_response") return new StewardContractError({
|
|
444
|
-
httpStatus: failure.status,
|
|
445
|
-
...context
|
|
446
|
-
});
|
|
447
|
-
return new StewardApiError({
|
|
448
|
-
code: failure.error,
|
|
449
|
-
httpStatus: failure.status,
|
|
450
|
-
...failure.message !== void 0 ? { message: failure.message } : {},
|
|
451
|
-
...failure.category !== void 0 ? { category: failure.category } : {},
|
|
452
|
-
...failure.retryable !== void 0 ? { retryable: failure.retryable } : {},
|
|
453
|
-
...context
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
async function run(method, call, attempt) {
|
|
457
|
-
let effective = {};
|
|
458
|
-
if (method !== "GET") {
|
|
459
|
-
const actorRef = call?.actorRef ?? options.actorRef;
|
|
460
|
-
if (actorRef === void 0) throw new StewardConfigError({
|
|
461
|
-
code: "actor_ref_required",
|
|
462
|
-
message: "actorRef yok: createSteward({ actorRef }) ya da çağrıda { actorRef } verin (tür[:kimlik], ör. user:42, system)"
|
|
463
|
-
});
|
|
464
|
-
if (!ActorRefSchema.safeParse(actorRef).success) throw new StewardConfigError({
|
|
465
|
-
code: "invalid_actor_ref",
|
|
466
|
-
message: "actorRef biçimi geçersiz (tür[:kimlik], ör. user:42, system)"
|
|
467
|
-
});
|
|
468
|
-
effective = {
|
|
469
|
-
idempotencyKey: call?.idempotencyKey ?? globalThis.crypto.randomUUID(),
|
|
470
|
-
actorRef
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
const result = await withRetries({
|
|
474
|
-
method,
|
|
475
|
-
...effective.idempotencyKey !== void 0 ? { idempotencyKey: effective.idempotencyKey } : {}
|
|
476
|
-
}, policy, () => attempt(effective));
|
|
477
|
-
if (result.ok) return result.data;
|
|
478
|
-
throw toError(result);
|
|
479
|
-
}
|
|
480
|
-
const read = (attempt) => run("GET", void 0, attempt);
|
|
481
|
-
const productApi = {
|
|
482
|
-
accounts: {
|
|
483
|
-
upsert: (ref, input, call) => run("PUT", call, (c) => raw.upsertAccount(ref, input, c)),
|
|
484
|
-
get: (ref, opts = {}) => read(() => raw.getAccount(ref, opts.include)),
|
|
485
|
-
state: async (ref) => {
|
|
486
|
-
const result = await read(() => raw.getAccountState(ref));
|
|
487
|
-
if (result.notModified) throw new StewardContractError({ httpStatus: 304 });
|
|
488
|
-
return result.state;
|
|
489
|
-
},
|
|
490
|
-
readState: (ref, opts = {}) => read(() => raw.getAccountState(ref, opts.etag !== void 0 ? { etag: opts.etag } : {})),
|
|
491
|
-
setPlan: (ref, input, call) => run("PUT", call, (c) => raw.setPlan(ref, input, c)),
|
|
492
|
-
resync: (ref, call) => run("POST", call, (c) => raw.resync(ref, c)),
|
|
493
|
-
delete: (ref, call) => run("DELETE", call, (c) => raw.deleteAccount(ref, c))
|
|
494
|
-
},
|
|
495
|
-
checkoutSessions: {
|
|
496
|
-
create: (ref, input, call) => run("POST", call, (c) => raw.createCheckoutSession(ref, input, c)),
|
|
497
|
-
get: (id) => read(() => raw.getCheckoutSession(id))
|
|
498
|
-
},
|
|
499
|
-
portalSessions: { create: (ref, input = {}, call) => run("POST", call, (c) => raw.createPortalSession(ref, input, c)) },
|
|
500
|
-
subscriptions: { cancel: (id, input, call) => run("POST", call, (c) => raw.cancelSubscription(id, input, c)) },
|
|
501
|
-
grants: {
|
|
502
|
-
create: (ref, input, call) => run("POST", call, (c) => raw.createGrant(ref, input, c)),
|
|
503
|
-
revoke: async (id, call) => {
|
|
504
|
-
await run("DELETE", call, (c) => raw.revokeGrant(id, c));
|
|
505
|
-
}
|
|
506
|
-
},
|
|
507
|
-
invoices: {
|
|
508
|
-
issue: (id, input, call) => run("POST", call, (c) => raw.issueInvoice(id, input, c)),
|
|
509
|
-
document: (id) => read(() => raw.getInvoiceDocument(id)),
|
|
510
|
-
createManual: (ref, input, call) => run("POST", call, (c) => raw.createManualInvoice(ref, input, c)),
|
|
511
|
-
void: (id, input, call) => run("POST", call, (c) => raw.voidInvoice(id, input, c))
|
|
512
|
-
},
|
|
513
|
-
catalog: {
|
|
514
|
-
get: () => read(() => raw.getCatalog()),
|
|
515
|
-
read: (opts = {}) => read(() => readCatalog(opts.etag))
|
|
516
|
-
},
|
|
517
|
-
stats: () => read(() => raw.getStats())
|
|
518
|
-
};
|
|
519
|
-
return {
|
|
520
|
-
...productTarget !== null ? productApi : rejectAll(productApi, missingProduct),
|
|
521
|
-
me: () => read(() => sendParsed(eitherTarget, "GET", "/v1/me", MeSchema, {})),
|
|
522
|
-
doctor: () => read(() => eitherClient.doctor()),
|
|
523
|
-
admin: {
|
|
524
|
-
catalog: { sync: async (input, call) => {
|
|
525
|
-
const { client } = requireAdmin();
|
|
526
|
-
return run("PUT", call, (c) => client.syncCatalog(input, c));
|
|
527
|
-
} },
|
|
528
|
-
accounts: { refresh: async (call) => {
|
|
529
|
-
const { target } = requireAdmin();
|
|
530
|
-
return run("POST", call, (c) => sendParsed(target, "POST", "/v1/admin/accounts/refresh", AccountsRefreshResultSchema, c));
|
|
531
|
-
} },
|
|
532
|
-
settings: {
|
|
533
|
-
get: async () => {
|
|
534
|
-
const { client } = requireAdmin();
|
|
535
|
-
return read(() => client.getSettings());
|
|
536
|
-
},
|
|
537
|
-
update: async (patch, call) => {
|
|
538
|
-
const { client } = requireAdmin();
|
|
539
|
-
return run("PATCH", call, (c) => client.updateSettings(patch, c));
|
|
540
|
-
}
|
|
541
|
-
},
|
|
542
|
-
endpoints: {
|
|
543
|
-
list: async () => {
|
|
544
|
-
const { client } = requireAdmin();
|
|
545
|
-
return read(() => client.listWebhookEndpoints());
|
|
546
|
-
},
|
|
547
|
-
create: async (input, call) => {
|
|
548
|
-
const { client } = requireAdmin();
|
|
549
|
-
return run("POST", call, (c) => client.createWebhookEndpoint(input, c));
|
|
550
|
-
},
|
|
551
|
-
rotate: async (id, input, call) => {
|
|
552
|
-
const { client } = requireAdmin();
|
|
553
|
-
return run("POST", call, (c) => client.rotateWebhookEndpointSecret(id, input, c));
|
|
554
|
-
},
|
|
555
|
-
deactivate: async (id, call) => {
|
|
556
|
-
const { client } = requireAdmin();
|
|
557
|
-
return run("POST", call, (c) => client.deactivateWebhookEndpoint(id, c));
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
//#endregion
|
|
564
|
-
export { createSteward as t };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"apiVersion": "2026-10-01",
|
|
3
|
-
"sha256": "00b36567f8972e90997f6436efcaab7e75e1104301ace40941ba3eaaea908582",
|
|
4
|
-
"additions": [
|
|
5
|
-
{
|
|
6
|
-
"since": "0.2.0",
|
|
7
|
-
"files": [
|
|
8
|
-
"account.state_changed.json"
|
|
9
|
-
],
|
|
10
|
-
"sha256": "af2584923b587bd1d50f8aeceaca4617e76a328d092a0f08cf5b8306fe0bd2d4"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"since": "F4f",
|
|
14
|
-
"files": [
|
|
15
|
-
"invoice.voided.json"
|
|
16
|
-
],
|
|
17
|
-
"sha256": "26bde921ce073e6f1634612b0b8d122835972647c8feb4608f54e776559cdeaf"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"since": "F4e",
|
|
21
|
-
"files": [
|
|
22
|
-
"account.deleted.json"
|
|
23
|
-
],
|
|
24
|
-
"sha256": "68c3507812304c790d73ccaffa82389c7fd25c9be85a1ca405722597921c8b32"
|
|
25
|
-
}
|
|
26
|
-
]
|
|
27
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "evt_0192f1a0-0000-7000-8000-000000000030",
|
|
3
|
-
"type": "account.deleted",
|
|
4
|
-
"apiVersion": "2026-10-01",
|
|
5
|
-
"createdAt": "2026-10-05T09:00:00Z",
|
|
6
|
-
"product": "example",
|
|
7
|
-
"accountRef": "acct_42",
|
|
8
|
-
"data": {
|
|
9
|
-
"account": {
|
|
10
|
-
"ref": "acct_42",
|
|
11
|
-
"displayName": "",
|
|
12
|
-
"version": 9,
|
|
13
|
-
"updatedAt": "2026-10-05T09:00:00Z",
|
|
14
|
-
"planCode": "free",
|
|
15
|
-
"accessState": "active",
|
|
16
|
-
"entitlements": {
|
|
17
|
-
"max_users": 3,
|
|
18
|
-
"max_devices": 15,
|
|
19
|
-
"max_networks_per_account": 1,
|
|
20
|
-
"max_approved_routes": 0,
|
|
21
|
-
"max_exit_nodes": 0
|
|
22
|
-
},
|
|
23
|
-
"catalogVersion": "3f2a9c0e7b41d8a65c09e1f4b7d2a8c3",
|
|
24
|
-
"subscription": null,
|
|
25
|
-
"openCheckout": null,
|
|
26
|
-
"profile": {
|
|
27
|
-
"present": false,
|
|
28
|
-
"kind": null
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"detail": {
|
|
32
|
-
"deletedAt": "2026-10-05T09:00:00Z",
|
|
33
|
-
"actorRef": "user:7"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "evt_0192f1a0-0000-7000-8000-000000000020",
|
|
3
|
-
"type": "account.state_changed",
|
|
4
|
-
"apiVersion": "2026-10-01",
|
|
5
|
-
"createdAt": "2026-10-01T09:00:00Z",
|
|
6
|
-
"product": "example",
|
|
7
|
-
"accountRef": "acct_42",
|
|
8
|
-
"data": {
|
|
9
|
-
"account": {
|
|
10
|
-
"ref": "acct_42",
|
|
11
|
-
"displayName": "Örnek A.Ş.",
|
|
12
|
-
"version": 2,
|
|
13
|
-
"updatedAt": "2026-10-01T09:00:00Z",
|
|
14
|
-
"planCode": "team",
|
|
15
|
-
"accessState": "active",
|
|
16
|
-
"entitlements": {
|
|
17
|
-
"max_users": null,
|
|
18
|
-
"max_devices": 250,
|
|
19
|
-
"max_networks_per_account": 3,
|
|
20
|
-
"max_approved_routes": 10,
|
|
21
|
-
"max_exit_nodes": 1
|
|
22
|
-
},
|
|
23
|
-
"catalogVersion": "3f2a9c0e7b41d8a65c09e1f4b7d2a8c3",
|
|
24
|
-
"subscription": {
|
|
25
|
-
"id": "0192f1a0-0000-7000-8000-000000000001",
|
|
26
|
-
"status": "past_due",
|
|
27
|
-
"planCode": "team",
|
|
28
|
-
"interval": "month",
|
|
29
|
-
"amountMinor": 125000,
|
|
30
|
-
"currency": "TRY",
|
|
31
|
-
"currentPeriodEnd": "2026-10-01T09:00:00Z",
|
|
32
|
-
"cancelAt": null,
|
|
33
|
-
"cancelAtPeriodEnd": false,
|
|
34
|
-
"pastDueSince": "2026-10-01T09:00:00Z",
|
|
35
|
-
"currentPeriodStart": "2026-09-01T09:00:00Z",
|
|
36
|
-
"dunning": {
|
|
37
|
-
"step": 0,
|
|
38
|
-
"nextActionAt": "2026-10-02T09:00:00Z"
|
|
39
|
-
},
|
|
40
|
-
"scheduledChange": null
|
|
41
|
-
},
|
|
42
|
-
"openCheckout": null,
|
|
43
|
-
"profile": {
|
|
44
|
-
"present": true,
|
|
45
|
-
"kind": "company"
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
"detail": {
|
|
49
|
-
"cause": "subscription.payment_failed",
|
|
50
|
-
"causeEventId": "evt_0192f1a0-0000-7000-8000-000000000016"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|