@cosmicdrift/kumiko-samples 0.392.2 → 0.394.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/package.json +1 -1
- package/packages/bundled-features/package.json +15 -5
- package/packages/bundled-features/src/account-security/README.md +1 -1
- package/packages/bundled-features/src/account-security/changes.json +6 -0
- package/packages/bundled-features/src/account-security/feature.ts +9 -1
- package/packages/bundled-features/src/audit/event-classification.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/changes.json +6 -0
- package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +10 -52
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +4 -4
- package/packages/bundled-features/src/auth-email-password/web/auth-redirect.ts +3 -12
- package/packages/bundled-features/src/auth-email-password/web/index.ts +2 -0
- package/packages/bundled-features/src/auth-email-password/web/login-failure.ts +54 -0
- package/packages/bundled-features/src/auth-email-password/web/login-url.ts +19 -0
- package/packages/bundled-features/src/auth-email-password/web/static-login.ts +183 -0
- package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +7 -63
- package/packages/bundled-features/src/billing-foundation/changes.json +30 -0
- package/packages/bundled-features/src/billing-foundation/checkout-core.ts +8 -16
- package/packages/bundled-features/src/billing-foundation/events.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +9 -2
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/index.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/one-off-checkout-gate.ts +44 -0
- package/packages/bundled-features/src/billing-foundation/one-off-customer.ts +17 -0
- package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +1 -0
- package/packages/bundled-features/src/billing-foundation/types.ts +26 -0
- package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +1 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-switch-confirm-dialog.tsx +8 -2
- package/packages/bundled-features/src/billing-foundation/web/plan-switch-recurring.ts +23 -0
- package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -0
- package/packages/bundled-features/src/cap-overview/changes.json +6 -0
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +3 -3
- package/packages/bundled-features/src/cap-overview/types.ts +2 -0
- package/packages/bundled-features/src/compliance-profiles/web/compliance-profile-catalog.tsx +10 -1
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +12 -3
- package/packages/bundled-features/src/files/changes.json +18 -0
- package/packages/bundled-features/src/inbound-mail-foundation/account-health.ts +146 -0
- package/packages/bundled-features/src/inbound-mail-foundation/account-poll-schedule.ts +74 -0
- package/packages/bundled-features/src/inbound-mail-foundation/account-poll.ts +238 -0
- package/packages/bundled-features/src/inbound-mail-foundation/account-record.ts +2 -0
- package/packages/bundled-features/src/inbound-mail-foundation/account-wake.ts +21 -0
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +155 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-callback-verify.ts +65 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-route-responses.ts +26 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +93 -62
- package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +66 -3
- package/packages/bundled-features/src/inbound-mail-foundation/content-refetch.ts +137 -0
- package/packages/bundled-features/src/inbound-mail-foundation/describe-error.ts +5 -0
- package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +45 -2
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +69 -2
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +133 -118
- package/packages/bundled-features/src/inbound-mail-foundation/file-storage-failure.ts +23 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-address.ts +79 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/acknowledge-quarantine.write.ts +55 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/attach-message-content.write.ts +184 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +137 -84
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +65 -45
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message-prepare.ts +177 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message-schema.ts +42 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +79 -160
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +93 -30
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +46 -30
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-quarantine.query.ts +106 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/mark-credential-stored.write.ts +47 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/message-content.query.ts +99 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/message-content.ts +229 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/message-detail.query.ts +34 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/reactivate-account.write.ts +129 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/retry-quarantined-message.write.ts +49 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +48 -9
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/set-trusted-authserv-id.write.ts +66 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/thread-rollup.ts +136 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/trusted-authserv-id.ts +4 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +82 -60
- package/packages/bundled-features/src/inbound-mail-foundation/i18n.ts +80 -1
- package/packages/bundled-features/src/inbound-mail-foundation/inbound-oauth-flow.ts +48 -0
- package/packages/bundled-features/src/inbound-mail-foundation/index.ts +94 -15
- package/packages/bundled-features/src/inbound-mail-foundation/ingest-failures.ts +137 -0
- package/packages/bundled-features/src/inbound-mail-foundation/ingest-payload.ts +46 -0
- package/packages/bundled-features/src/inbound-mail-foundation/mail-roles.ts +132 -0
- package/packages/bundled-features/src/inbound-mail-foundation/normalize-inbound-message.ts +119 -0
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +26 -8
- package/packages/bundled-features/src/inbound-mail-foundation/outbound-attachment-access.ts +28 -0
- package/packages/bundled-features/src/inbound-mail-foundation/outbound-attachments.ts +111 -0
- package/packages/bundled-features/src/inbound-mail-foundation/outbound-mail.ts +19 -1
- package/packages/bundled-features/src/inbound-mail-foundation/probe.ts +2 -1
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +33 -2
- package/packages/bundled-features/src/inbound-mail-foundation/provider-errors.ts +74 -0
- package/packages/bundled-features/src/inbound-mail-foundation/quarantine-entities.ts +56 -0
- package/packages/bundled-features/src/inbound-mail-foundation/quarantine-header-import.ts +63 -0
- package/packages/bundled-features/src/inbound-mail-foundation/quarantine-retry.ts +137 -0
- package/packages/bundled-features/src/inbound-mail-foundation/quarantine-screens.ts +66 -0
- package/packages/bundled-features/src/inbound-mail-foundation/quarantine.ts +72 -0
- package/packages/bundled-features/src/inbound-mail-foundation/retention-job.ts +78 -0
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +43 -8
- package/packages/bundled-features/src/inbound-mail-foundation/screens.ts +239 -79
- package/packages/bundled-features/src/inbound-mail-foundation/send-from-account.ts +24 -5
- package/packages/bundled-features/src/inbound-mail-foundation/sender-auth.ts +215 -0
- package/packages/bundled-features/src/inbound-mail-foundation/supervisor-ingest.ts +311 -0
- package/packages/bundled-features/src/inbound-mail-foundation/sync-account-now.ts +61 -0
- package/packages/bundled-features/src/inbound-mail-foundation/sync-cursor-store.ts +79 -0
- package/packages/bundled-features/src/inbound-mail-foundation/syncable-accounts.ts +123 -0
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +5 -1
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +76 -105
- package/packages/bundled-features/src/inbound-mail-foundation/wake-consumer.ts +33 -0
- package/packages/bundled-features/src/inbound-mail-foundation/watch-catch-up.ts +68 -0
- package/packages/bundled-features/src/inbound-mail-foundation/watch-lease.ts +124 -0
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor-types.ts +87 -0
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +143 -433
- package/packages/bundled-features/src/inbound-mail-foundation/web/client-plugin.ts +73 -0
- package/packages/bundled-features/src/inbound-mail-foundation/web/constants.ts +7 -0
- package/packages/bundled-features/src/inbound-mail-foundation/web/index.ts +5 -0
- package/packages/bundled-features/src/inbound-provider-imap/changes.json +42 -0
- package/packages/bundled-features/src/inbound-provider-imap/connect-mailbox.ts +155 -63
- package/packages/bundled-features/src/inbound-provider-imap/connect-my-mailbox.ts +45 -0
- package/packages/bundled-features/src/inbound-provider-imap/constants.ts +14 -1
- package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +2 -1
- package/packages/bundled-features/src/inbound-provider-imap/download-batch.ts +38 -0
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +75 -44
- package/packages/bundled-features/src/inbound-provider-imap/fetch-content.ts +84 -0
- package/packages/bundled-features/src/inbound-provider-imap/i18n.ts +28 -2
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +35 -84
- package/packages/bundled-features/src/inbound-provider-imap/index.ts +1 -2
- package/packages/bundled-features/src/inbound-provider-imap/mime-content.ts +85 -0
- package/packages/bundled-features/src/inbound-provider-imap/parse-message.ts +149 -0
- package/packages/bundled-features/src/inbound-provider-imap/reconnect-account.ts +45 -0
- package/packages/bundled-features/src/inbound-provider-imap/reconnect-mailbox.ts +103 -0
- package/packages/bundled-features/src/inbound-provider-imap/reconnect-prefill.query.ts +49 -0
- package/packages/bundled-features/src/inbound-provider-imap/screens.ts +209 -42
- package/packages/bundled-features/src/inbound-provider-imap/send-mail.ts +11 -2
- package/packages/bundled-features/src/inbound-provider-imap/sender-auth-headers.ts +36 -0
- package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +75 -3
- package/packages/bundled-features/src/inbound-provider-inmemory/index.ts +6 -0
- package/packages/bundled-features/src/jobs/changes.json +18 -0
- package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -0
- package/packages/bundled-features/src/subscription-paddle/changes.json +15 -0
- package/packages/bundled-features/src/subscription-paddle/checkout-config.query.ts +17 -0
- package/packages/bundled-features/src/subscription-paddle/checkout-methods.ts +107 -0
- package/packages/bundled-features/src/subscription-paddle/checkout-page.ts +154 -0
- package/packages/bundled-features/src/subscription-paddle/client.ts +78 -0
- package/packages/bundled-features/src/subscription-paddle/constants.ts +32 -0
- package/packages/bundled-features/src/subscription-paddle/feature.ts +265 -0
- package/packages/bundled-features/src/subscription-paddle/index.ts +17 -0
- package/packages/bundled-features/src/subscription-paddle/plan-switch-methods.ts +135 -0
- package/packages/bundled-features/src/subscription-paddle/price-methods.ts +148 -0
- package/packages/bundled-features/src/subscription-paddle/runtime.ts +203 -0
- package/packages/bundled-features/src/subscription-paddle/subscription-state.ts +73 -0
- package/packages/bundled-features/src/subscription-paddle/tenant-binding.ts +34 -0
- package/packages/bundled-features/src/subscription-paddle/verify-webhook.ts +61 -0
- package/packages/bundled-features/src/subscription-paddle/webhook-events.ts +172 -0
- package/packages/bundled-features/src/template-resolver/changes.json +12 -0
- package/packages/bundled-features/src/template-resolver/handlers/collection-item.query.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/collection-list.query.ts +6 -2
- package/packages/bundled-features/src/template-resolver/handlers/collection-shared.ts +25 -0
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +218 -117
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +1 -2
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +2 -1
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-constants.ts +4 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +11 -5
- package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +2 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +89 -39
- package/samples/apps/use-all-bundled/kumiko/migrations/0056_inbound-mail-quarantine-retry.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/package.json +1 -0
- package/samples/apps/use-all-bundled/playwright.billing-docs.config.ts +15 -0
- package/samples/apps/use-all-bundled/src/app/cap-overview-caps.ts +16 -2
- package/samples/apps/use-all-bundled/src/app/cap-overview-labels-feature.ts +2 -1
- package/samples/apps/use-all-bundled/src/app/client.tsx +2 -0
- package/samples/apps/use-all-bundled/src/app/screens-feature.ts +14 -2
- package/samples/apps/use-all-bundled/src/app/seed.ts +4 -1
- package/samples/apps/use-all-bundled/src/run-config.ts +7 -5
- package/samples/recipes/cross-feature-events/README.md +10 -0
- package/samples/recipes/cross-feature-events/src/feature.ts +14 -0
- package/samples/recipes/dashboard-stats/README.md +8 -0
- package/samples/recipes/dashboard-stats/src/feature.ts +91 -1
- package/samples/recipes/env-composition/README.md +2 -2
- package/samples/recipes/env-composition/src/env.ts +2 -2
- package/samples/recipes/inbound-mail-triage/README.md +12 -0
- package/samples/recipes/pdf-audit-export/src/feature.ts +71 -48
- package/packages/bundled-features/src/subscription-dodo/changes.json +0 -14
- package/packages/bundled-features/src/subscription-dodo/client.ts +0 -45
- package/packages/bundled-features/src/subscription-dodo/constants.ts +0 -24
- package/packages/bundled-features/src/subscription-dodo/feature.ts +0 -207
- package/packages/bundled-features/src/subscription-dodo/index.ts +0 -16
- package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +0 -345
- package/packages/bundled-features/src/subscription-dodo/runtime.ts +0 -126
- package/packages/bundled-features/src/subscription-dodo/verify-webhook.ts +0 -262
|
@@ -24,6 +24,18 @@ mail-triage ← this sample (the app-side cons
|
|
|
24
24
|
3. The handler-trigger fans the write out to the worker lane; the triage job records `{ from, subject, scope, threadHint }`.
|
|
25
25
|
4. A replay of the same `providerMessageId` reports `duplicate: true`, creates no second row — and the keyed triage store stays at one item.
|
|
26
26
|
|
|
27
|
+
## Sign-in errors
|
|
28
|
+
|
|
29
|
+
When the provider rejects the stored login, the supervisor sets the account to `auth_error` and stops fetching. With `inbound-provider-imap` mounted, the mail accounts list offers **Reconnect** on that row: the form `mail-account-reconnect-imap` is prefilled with the stored server settings (never the password), `inbound-provider-imap:write:reconnect-mailbox` checks the new login and puts the same account back to `active`. Threads, sync cursor and seen messages stay, so the next poll continues where the account stopped instead of ingesting the mailbox again. The cases are covered in `inbound-provider-imap/__tests__/reconnect-mailbox.integration.test.ts`; they need greenmail, which this sample's in-memory provider does not.
|
|
30
|
+
|
|
31
|
+
## Mail content, quarantine and status
|
|
32
|
+
|
|
33
|
+
Every ingested mail now carries its full text (`bodyText`, at most 32,000 characters, flagged by `bodyTextTruncated`), the original (`.eml`, or `.html` for HTML-only mails) and its attachments as file references; `inbound-mail-foundation:query:message:content` returns them, while `message:list` stays light and never carries the body. Retention removes the mail row but keeps the attachment files. Needs file storage in the app; without it the mail is still imported and the mailbox shows **Unterbrochen** (`file_storage_unavailable`).
|
|
34
|
+
|
|
35
|
+
A mail the provider cannot parse is set aside in quarantine instead of blocking the mailbox: the rest imports, the sync cursor moves on, the account shows `degraded` with `lastError: message_quarantined` and the count, and `acknowledge-quarantine` clears it. Three failed polls in a row (`connection_failed`, `rate_limited`) also mark the mailbox `degraded` until the next success.
|
|
36
|
+
|
|
37
|
+
A disconnected mailbox keeps its account id: `reactivate-account` (or connecting the same OAuth address again) puts it back to `active` with its cursor, so nothing is imported twice, and the mail arrives right away instead of at the next poll. Connecting an address that is already connected is refused with 409.
|
|
38
|
+
|
|
27
39
|
## Tests
|
|
28
40
|
|
|
29
41
|
- E2E through `createAllInOneEntrypoint` (HTTP → dispatcher → BullMQ worker lane, no framework-internal shortcuts): connect → ingest → triage item appears.
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
// FileRef: renderTablePdf draws the table (pure JS, no browser). The storage
|
|
5
5
|
// write runs in the beforeTransaction phase, so the transaction holds no pool
|
|
6
6
|
// connection while the bytes upload; the handler then only creates the
|
|
7
|
-
// fileRef row from event.prepared.
|
|
7
|
+
// fileRef row from event.prepared. Called from another handler (a nested
|
|
8
|
+
// write) there is no such phase, so the handler renders and stores inside the
|
|
9
|
+
// caller's transaction instead.
|
|
8
10
|
|
|
9
11
|
import { renderTablePdf } from "@cosmicdrift/kumiko-bundled-features/renderer-pdf";
|
|
10
12
|
import {
|
|
@@ -19,6 +21,7 @@ import {
|
|
|
19
21
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
20
22
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
21
23
|
import {
|
|
24
|
+
createFileRefFromBytes,
|
|
22
25
|
createFileRefFromUploadedBytes,
|
|
23
26
|
uploadFileBytesBeforeTransaction,
|
|
24
27
|
} from "@cosmicdrift/kumiko-framework/files";
|
|
@@ -60,6 +63,62 @@ export const pdfAuditExportFeature = defineFeature("pdf-audit-export", (r) => {
|
|
|
60
63
|
r.entity({ name: "inspection", ...inspectionEntity });
|
|
61
64
|
const { executor: inspectionExecutor } = createEntityExecutor("inspection", inspectionEntity);
|
|
62
65
|
|
|
66
|
+
type AuditPayload = { assetName: string; from: string; to: string };
|
|
67
|
+
type ExecutorUser = Parameters<typeof inspectionExecutor.list>[1];
|
|
68
|
+
type ExecutorDb = Parameters<typeof inspectionExecutor.list>[2];
|
|
69
|
+
|
|
70
|
+
async function renderAudit(payload: AuditPayload, user: ExecutorUser, db: ExecutorDb) {
|
|
71
|
+
const { assetName, from, to } = payload;
|
|
72
|
+
const inspections = await inspectionExecutor.list(
|
|
73
|
+
{
|
|
74
|
+
filters: [
|
|
75
|
+
{ field: "assetName", op: "eq", value: assetName },
|
|
76
|
+
{ field: "inspectedOn", op: "gte", value: from },
|
|
77
|
+
{ field: "inspectedOn", op: "lte", value: to },
|
|
78
|
+
],
|
|
79
|
+
sort: "inspectedOn",
|
|
80
|
+
sortDirection: "asc",
|
|
81
|
+
limit: AUDIT_ROW_LIMIT + 1,
|
|
82
|
+
},
|
|
83
|
+
user,
|
|
84
|
+
db,
|
|
85
|
+
);
|
|
86
|
+
// A protocol that silently drops inspections would look complete.
|
|
87
|
+
if (inspections.rows.length > AUDIT_ROW_LIMIT) {
|
|
88
|
+
return writeFailure(
|
|
89
|
+
new UnprocessableError("audit_period_too_large", {
|
|
90
|
+
details: { limit: AUDIT_ROW_LIMIT },
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const { pdfBytes } = await renderTablePdf({
|
|
96
|
+
title: `Prüfprotokoll ${assetName}`,
|
|
97
|
+
meta: [
|
|
98
|
+
{ label: "Zeitraum", value: `${formatGermanDate(from)} bis ${formatGermanDate(to)}` },
|
|
99
|
+
{ label: "Prüfungen", value: String(inspections.rows.length) },
|
|
100
|
+
],
|
|
101
|
+
columns: [{ header: "Datum" }, { header: "Ergebnis" }, { header: "Prüfer", weight: 2 }],
|
|
102
|
+
rows: inspections.rows.map((row) => [
|
|
103
|
+
formatGermanDate(String(row["inspectedOn"])),
|
|
104
|
+
RESULT_LABELS[z.enum(INSPECTION_RESULTS).parse(row["result"])],
|
|
105
|
+
String(row["inspector"] ?? ""),
|
|
106
|
+
]),
|
|
107
|
+
emptyText: "Keine Prüfungen im Zeitraum",
|
|
108
|
+
footerText: `Prüfbericht ${assetName}`,
|
|
109
|
+
pageLabel: "Seite {page} von {pages}",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
isSuccess: true as const,
|
|
114
|
+
data: {
|
|
115
|
+
fileName: `audit-${toFileNameSegment(assetName)}.pdf`,
|
|
116
|
+
mimeType: "application/pdf",
|
|
117
|
+
data: pdfBytes,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
63
122
|
r.writeHandler({
|
|
64
123
|
name: "inspection:create",
|
|
65
124
|
schema: z.object({
|
|
@@ -77,56 +136,20 @@ export const pdfAuditExportFeature = defineFeature("pdf-audit-export", (r) => {
|
|
|
77
136
|
name: "export-audit",
|
|
78
137
|
schema: z.object({ assetName: z.string().min(1), from: z.iso.date(), to: z.iso.date() }),
|
|
79
138
|
access: access.access,
|
|
139
|
+
// Nested in another command there is no phase of its own; the handler then does this work itself.
|
|
140
|
+
beforeTransactionWhenNested: "skip",
|
|
80
141
|
beforeTransaction: async (event, ctx) => {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
filters: [
|
|
85
|
-
{ field: "assetName", op: "eq", value: assetName },
|
|
86
|
-
{ field: "inspectedOn", op: "gte", value: from },
|
|
87
|
-
{ field: "inspectedOn", op: "lte", value: to },
|
|
88
|
-
],
|
|
89
|
-
sort: "inspectedOn",
|
|
90
|
-
sortDirection: "asc",
|
|
91
|
-
limit: AUDIT_ROW_LIMIT + 1,
|
|
92
|
-
},
|
|
93
|
-
event.user,
|
|
94
|
-
ctx.db,
|
|
95
|
-
);
|
|
96
|
-
// A protocol that silently drops inspections would look complete.
|
|
97
|
-
if (inspections.rows.length > AUDIT_ROW_LIMIT) {
|
|
98
|
-
return writeFailure(
|
|
99
|
-
new UnprocessableError("audit_period_too_large", {
|
|
100
|
-
details: { limit: AUDIT_ROW_LIMIT },
|
|
101
|
-
}),
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const { pdfBytes } = await renderTablePdf({
|
|
106
|
-
title: `Prüfprotokoll ${assetName}`,
|
|
107
|
-
meta: [
|
|
108
|
-
{ label: "Zeitraum", value: `${formatGermanDate(from)} bis ${formatGermanDate(to)}` },
|
|
109
|
-
{ label: "Prüfungen", value: String(inspections.rows.length) },
|
|
110
|
-
],
|
|
111
|
-
columns: [{ header: "Datum" }, { header: "Ergebnis" }, { header: "Prüfer", weight: 2 }],
|
|
112
|
-
rows: inspections.rows.map((row) => [
|
|
113
|
-
formatGermanDate(String(row["inspectedOn"])),
|
|
114
|
-
RESULT_LABELS[z.enum(INSPECTION_RESULTS).parse(row["result"])],
|
|
115
|
-
String(row["inspector"] ?? ""),
|
|
116
|
-
]),
|
|
117
|
-
emptyText: "Keine Prüfungen im Zeitraum",
|
|
118
|
-
footerText: `Prüfbericht ${assetName}`,
|
|
119
|
-
pageLabel: "Seite {page} von {pages}",
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
return uploadFileBytesBeforeTransaction(ctx, {
|
|
123
|
-
fileName: `audit-${toFileNameSegment(assetName)}.pdf`,
|
|
124
|
-
mimeType: "application/pdf",
|
|
125
|
-
data: pdfBytes,
|
|
126
|
-
});
|
|
142
|
+
const rendered = await renderAudit(event.payload, event.user, ctx.db);
|
|
143
|
+
if (!rendered.isSuccess) return rendered;
|
|
144
|
+
return uploadFileBytesBeforeTransaction(ctx, rendered.data);
|
|
127
145
|
},
|
|
128
146
|
handler: async (event, ctx) => {
|
|
129
|
-
const created =
|
|
147
|
+
const created =
|
|
148
|
+
event.prepared !== undefined
|
|
149
|
+
? await createFileRefFromUploadedBytes(ctx, event.prepared)
|
|
150
|
+
: await renderAudit(event.payload, event.user, ctx.db).then((rendered) =>
|
|
151
|
+
rendered.isSuccess ? createFileRefFromBytes(ctx, rendered.data) : rendered,
|
|
152
|
+
);
|
|
130
153
|
if (!created.isSuccess) return created;
|
|
131
154
|
return { isSuccess: true as const, data: { fileRefId: created.data.id } };
|
|
132
155
|
},
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"version": "0.388.0",
|
|
4
|
-
"type": "improvement",
|
|
5
|
-
"title": "New subscription provider for Dodo Payments",
|
|
6
|
-
"detail": "createSubscriptionDodoFeature() registers the \"dodo\" subscription provider (Merchant of Record). It verifies Standard Webhooks signatures, maps Dodo events to the foundation events, creates hosted checkouts and portal sessions, and switches plans in the app through previewPlanSwitch and applyPlanSwitch. Config keys are api-key, webhook-secret, billing-live and environment. Plan switches need a Product Collection in Dodo."
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
"version": "0.388.0",
|
|
10
|
-
"type": "improvement",
|
|
11
|
-
"title": "dodoEnvSchema declares the Dodo product id slots",
|
|
12
|
-
"detail": "dodoEnvSchema({ prices: [\"PRO\"] }) adds one optional DODO_PRODUCT_PRO slot per plan next to the API key and webhook key, and checks the pdt_ prefix. It is the counterpart to stripeEnvSchema."
|
|
13
|
-
}
|
|
14
|
-
]
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// Thin JSON-over-fetch client for the Dodo Payments REST API. All traffic goes
|
|
2
|
-
// through the injected `send`, which defaults to the SSRF-safe egress in
|
|
3
|
-
// feature.ts and is replaced by a mock in tests.
|
|
4
|
-
|
|
5
|
-
import { parseJsonOrThrow } from "@cosmicdrift/kumiko-framework/utils";
|
|
6
|
-
|
|
7
|
-
export type DodoSend = (url: string, init?: RequestInit) => Promise<Response>;
|
|
8
|
-
|
|
9
|
-
export type DodoRequest = {
|
|
10
|
-
readonly method: "GET" | "POST" | "PATCH";
|
|
11
|
-
/** Path segments interpolated into it must already be encodeURIComponent'ed. */
|
|
12
|
-
readonly path: string;
|
|
13
|
-
readonly query?: Readonly<Record<string, string>>;
|
|
14
|
-
readonly body?: unknown;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type DodoTarget = { readonly baseUrl: string; readonly apiKey: string };
|
|
18
|
-
|
|
19
|
-
// Never carries the response body or the key: both can hold customer data or secrets.
|
|
20
|
-
export class DodoHttpError extends Error {
|
|
21
|
-
readonly status: number;
|
|
22
|
-
constructor(request: DodoRequest, status: number) {
|
|
23
|
-
super(`subscription-dodo: ${request.method} ${request.path} failed with status ${status}`);
|
|
24
|
-
this.name = "DodoHttpError";
|
|
25
|
-
this.status = status;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function createDodoHttp(send: DodoSend) {
|
|
30
|
-
return async (target: DodoTarget, request: DodoRequest): Promise<unknown> => {
|
|
31
|
-
const query = request.query ? `?${new URLSearchParams(request.query).toString()}` : "";
|
|
32
|
-
const response = await send(`${target.baseUrl}${request.path}${query}`, {
|
|
33
|
-
method: request.method,
|
|
34
|
-
headers: {
|
|
35
|
-
Authorization: `Bearer ${target.apiKey}`,
|
|
36
|
-
Accept: "application/json",
|
|
37
|
-
...(request.body !== undefined && { "Content-Type": "application/json" }),
|
|
38
|
-
},
|
|
39
|
-
...(request.body !== undefined && { body: JSON.stringify(request.body) }),
|
|
40
|
-
});
|
|
41
|
-
if (!response.ok) throw new DodoHttpError(request, response.status);
|
|
42
|
-
const text = await response.text();
|
|
43
|
-
return text.length === 0 ? null : parseJsonOrThrow<unknown>(text, "subscription-dodo response");
|
|
44
|
-
};
|
|
45
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export const SUBSCRIPTION_DODO_FEATURE = "subscription-dodo" as const;
|
|
2
|
-
|
|
3
|
-
// Must match the path segment in the webhook URL `/api/subscription/webhook/dodo`.
|
|
4
|
-
export const DODO_PROVIDER_NAME = "dodo" as const;
|
|
5
|
-
|
|
6
|
-
// Config-key short-names, qualified to `subscription-dodo:config:<name>` at
|
|
7
|
-
// registry-build. api-key + webhook-secret are backing:"secrets".
|
|
8
|
-
export const DODO_API_KEY_CONFIG = "api-key" as const;
|
|
9
|
-
export const DODO_WEBHOOK_SECRET_CONFIG = "webhook-secret" as const;
|
|
10
|
-
export const DODO_BILLING_LIVE_CONFIG = "billingLive" as const;
|
|
11
|
-
export const DODO_ENVIRONMENT_CONFIG = "environment" as const;
|
|
12
|
-
|
|
13
|
-
// Separate from billingLive on purpose: staging runs Dodo's test API with
|
|
14
|
-
// billingLive on, production flips both.
|
|
15
|
-
export const DodoEnvironments = {
|
|
16
|
-
test: "test",
|
|
17
|
-
live: "live",
|
|
18
|
-
} as const;
|
|
19
|
-
export type DodoEnvironment = (typeof DodoEnvironments)[keyof typeof DodoEnvironments];
|
|
20
|
-
|
|
21
|
-
export const DODO_BASE_URLS: Readonly<Record<DodoEnvironment, string>> = {
|
|
22
|
-
test: "https://test.dodopayments.com",
|
|
23
|
-
live: "https://live.dodopayments.com",
|
|
24
|
-
};
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
// kumiko-feature-version: 1
|
|
2
|
-
//
|
|
3
|
-
// subscription-dodo — Dodo Payments (merchant of record) plugin for the
|
|
4
|
-
// billing-foundation plugin API. Credentials, the billing-live master switch and
|
|
5
|
-
// the API environment (test | live) are config keys resolved at call time, like
|
|
6
|
-
// subscription-stripe. `apiKey` / `webhookSecret` options are env→secrets bridge
|
|
7
|
-
// fallbacks.
|
|
8
|
-
|
|
9
|
-
import type { SubscriptionProviderPlugin } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
10
|
-
import {
|
|
11
|
-
access,
|
|
12
|
-
createSystemConfig,
|
|
13
|
-
defineFeature,
|
|
14
|
-
type FeatureDefinition,
|
|
15
|
-
} from "@cosmicdrift/kumiko-framework/engine";
|
|
16
|
-
import { type FeatureWithEnvSchema, featureWithEnvSchema } from "@cosmicdrift/kumiko-framework/env";
|
|
17
|
-
import { egress } from "@cosmicdrift/kumiko-framework/http";
|
|
18
|
-
import * as z from "zod";
|
|
19
|
-
import type { DodoSend } from "./client.ts";
|
|
20
|
-
import {
|
|
21
|
-
DODO_API_KEY_CONFIG,
|
|
22
|
-
DODO_BILLING_LIVE_CONFIG,
|
|
23
|
-
DODO_ENVIRONMENT_CONFIG,
|
|
24
|
-
DODO_PROVIDER_NAME,
|
|
25
|
-
DODO_WEBHOOK_SECRET_CONFIG,
|
|
26
|
-
DodoEnvironments,
|
|
27
|
-
SUBSCRIPTION_DODO_FEATURE,
|
|
28
|
-
} from "./constants.ts";
|
|
29
|
-
import {
|
|
30
|
-
createDodoApplyPlanSwitch,
|
|
31
|
-
createDodoCancelSubscription,
|
|
32
|
-
createDodoCheckoutSession,
|
|
33
|
-
createDodoPortalSession,
|
|
34
|
-
createDodoPreviewPlanSwitch,
|
|
35
|
-
createDodoPriceCache,
|
|
36
|
-
createDodoResumeSubscription,
|
|
37
|
-
createDodoRetrievePrices,
|
|
38
|
-
createDodoRetrieveSubscription,
|
|
39
|
-
} from "./plugin-methods.ts";
|
|
40
|
-
import { createDodoRuntimes } from "./runtime.ts";
|
|
41
|
-
import { verifyAndParseDodoWebhook } from "./verify-webhook.ts";
|
|
42
|
-
|
|
43
|
-
/** Bridge contract: both fields are optional fallbacks for the secrets store. */
|
|
44
|
-
export const subscriptionDodoEnvSchema = z.object({
|
|
45
|
-
DODO_PAYMENTS_API_KEY: z
|
|
46
|
-
.string()
|
|
47
|
-
.min(1)
|
|
48
|
-
.describe("Dodo Payments API key. Bridge-fallback — prefer the secret.")
|
|
49
|
-
.meta({ kumiko: { pulumi: { secret: true } } })
|
|
50
|
-
.optional(),
|
|
51
|
-
DODO_PAYMENTS_WEBHOOK_KEY: z
|
|
52
|
-
.string()
|
|
53
|
-
.min(1)
|
|
54
|
-
.describe(
|
|
55
|
-
"Dodo Payments webhook signing key (`whsec_...`). Bridge-fallback — prefer the secret.",
|
|
56
|
-
)
|
|
57
|
-
.meta({ kumiko: { pulumi: { secret: true } } })
|
|
58
|
-
.optional(),
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
type DodoProductEnvShape<TPrice extends string> = {
|
|
62
|
-
[Key in `DODO_PRODUCT_${TPrice}`]: z.ZodOptional<z.ZodString>;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
/** Dodo env block plus one `DODO_PRODUCT_<NAME>` slot per plan product the app
|
|
66
|
-
* maps to a tier, e.g. `dodoEnvSchema({ prices: ["PRO", "TEAM"] })`.
|
|
67
|
-
* Shares its bridge fields with `subscriptionDodoEnvSchema`, so spreading
|
|
68
|
-
* it into `composeEnvSchema({ extend })` next to the mounted feature is no
|
|
69
|
-
* key clash. */
|
|
70
|
-
export function dodoEnvSchema<const TPrice extends string>(options: {
|
|
71
|
-
readonly prices: readonly TPrice[];
|
|
72
|
-
}) {
|
|
73
|
-
const productShape = Object.fromEntries(
|
|
74
|
-
options.prices.map((price) => [
|
|
75
|
-
`DODO_PRODUCT_${price}`,
|
|
76
|
-
z
|
|
77
|
-
.string()
|
|
78
|
-
.regex(/^pdt_/, `DODO_PRODUCT_${price} must start with 'pdt_'`)
|
|
79
|
-
.optional()
|
|
80
|
-
.describe(`Dodo Payments product id (\`pdt_...\`) of the ${price} plan.`),
|
|
81
|
-
]),
|
|
82
|
-
// @cast-boundary schema-walk — Object.fromEntries widens to an index signature; the entries are exactly one optional string per listed price.
|
|
83
|
-
) as DodoProductEnvShape<TPrice>;
|
|
84
|
-
return z.object({ ...subscriptionDodoEnvSchema.shape, ...productShape });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export type SubscriptionDodoOptions = {
|
|
88
|
-
/** Bridge-fallback API key; the `api-key` config key wins when set. */
|
|
89
|
-
readonly apiKey?: string;
|
|
90
|
-
/** Bridge-fallback webhook secret; the `webhook-secret` config key wins when set. */
|
|
91
|
-
readonly webhookSecret?: string;
|
|
92
|
-
/** Dodo product id → app tier name. Products absent here drop the event. */
|
|
93
|
-
readonly priceToTier?: Readonly<Record<string, string>>;
|
|
94
|
-
/** Product ids a mode:"payment" checkout may target; empty = one-off checkouts closed. */
|
|
95
|
-
readonly oneOffPriceIds?: readonly string[];
|
|
96
|
-
/** HTTP transport; defaults to the SSRF-safe external egress. Test seam. */
|
|
97
|
-
readonly send?: DodoSend;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// Validated once at feature-build time so a typo fails app startup instead of
|
|
101
|
-
// silently widening or narrowing the one-off allowlist at request time.
|
|
102
|
-
function validateOneOffPriceIds(priceIds: readonly string[]): void {
|
|
103
|
-
const seen = new Set<string>();
|
|
104
|
-
for (const priceId of priceIds) {
|
|
105
|
-
if (priceId.length === 0) {
|
|
106
|
-
throw new Error("subscription-dodo: oneOffPriceIds must not contain an empty string");
|
|
107
|
-
}
|
|
108
|
-
if (seen.has(priceId)) {
|
|
109
|
-
throw new Error(`subscription-dodo: duplicate oneOffPriceIds entry "${priceId}"`);
|
|
110
|
-
}
|
|
111
|
-
seen.add(priceId);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function createSubscriptionDodoFeature(
|
|
116
|
-
options: SubscriptionDodoOptions = {},
|
|
117
|
-
): FeatureWithEnvSchema<FeatureDefinition, typeof subscriptionDodoEnvSchema.shape> {
|
|
118
|
-
validateOneOffPriceIds(options.oneOffPriceIds ?? []);
|
|
119
|
-
const feature: FeatureDefinition = defineFeature(SUBSCRIPTION_DODO_FEATURE, (r) => {
|
|
120
|
-
r.describe(
|
|
121
|
-
'Dodo Payments provider plugin for `billing-foundation`. Dodo acts as merchant of record: it sells to the customer and issues invoice and VAT, so the consumer-protection texts for this provider come from `consumerProtection.providerTexts`. Reads its API key and webhook secret from system config keys with `backing:"secrets"` (envelope-encrypted in the secrets store under the system tenant), plus a `billingLive` master switch and an `environment` key (test | live). `environment` is its own key because Dodo serves test and live mode from different API hosts and a live key must never hit the test host or the other way round; `billingLive` only decides whether checkouts are offered at all. All four are runtime config, so keys rotate and prod goes live without a redeploy, and the `mask` on each derives the sysadmin settings screen and nav. Mount via `createSubscriptionDodoFeature({ priceToTier })`; the optional `apiKey`/`webhookSecret` options are env to secrets bridge fallbacks (`DODO_PAYMENTS_API_KEY`, `DODO_PAYMENTS_WEBHOOK_KEY`). The plugin always mounts; `createCheckoutSession` throws `feature_disabled` unless `billingLive` is true. Webhooks arrive at `/api/subscription/webhook/dodo` and are verified per the Standard Webhooks spec (HMAC-SHA256 over id, timestamp and the raw body, 5 minute tolerance); events for products outside `priceToTier` or without tenant metadata are dropped. Implements hosted checkout, customer portal, cancel and resume, `retrievePrices` (10-minute TTL cache), `retrieveSubscription` for the sync job, and an in-app plan switch: `previewPlanSwitch` shows the proration before the customer confirms, `applyPlanSwitch` changes the plan without a hosted page and waits for the `plan_changed` webhook to move the tier. A plan switch in Dodo requires all plan products to belong to one Product Collection.',
|
|
122
|
-
);
|
|
123
|
-
r.uiHints({
|
|
124
|
-
displayLabel: "Billing · Dodo Payments",
|
|
125
|
-
category: "billing",
|
|
126
|
-
recommended: false,
|
|
127
|
-
});
|
|
128
|
-
r.requires({ features: ["billing-foundation"] });
|
|
129
|
-
r.requires({ features: ["config"] });
|
|
130
|
-
r.requires({ features: ["secrets"] });
|
|
131
|
-
r.envSchema(subscriptionDodoEnvSchema);
|
|
132
|
-
|
|
133
|
-
const configKeys = r.config({
|
|
134
|
-
keys: {
|
|
135
|
-
[DODO_API_KEY_CONFIG]: createSystemConfig("text", {
|
|
136
|
-
backing: "secrets",
|
|
137
|
-
write: access.systemAdmin,
|
|
138
|
-
read: access.admin,
|
|
139
|
-
mask: { title: "subscription-dodo.api-key", icon: "key", order: 1 },
|
|
140
|
-
}),
|
|
141
|
-
[DODO_WEBHOOK_SECRET_CONFIG]: createSystemConfig("text", {
|
|
142
|
-
backing: "secrets",
|
|
143
|
-
write: access.systemAdmin,
|
|
144
|
-
read: access.admin,
|
|
145
|
-
mask: { title: "subscription-dodo.webhook-secret", icon: "shield", order: 2 },
|
|
146
|
-
}),
|
|
147
|
-
[DODO_ENVIRONMENT_CONFIG]: createSystemConfig("select", {
|
|
148
|
-
default: DodoEnvironments.test,
|
|
149
|
-
options: [DodoEnvironments.test, DodoEnvironments.live],
|
|
150
|
-
write: access.systemAdmin,
|
|
151
|
-
read: access.admin,
|
|
152
|
-
mask: { title: "subscription-dodo.environment", icon: "server", order: 3 },
|
|
153
|
-
}),
|
|
154
|
-
[DODO_BILLING_LIVE_CONFIG]: createSystemConfig("boolean", {
|
|
155
|
-
default: false,
|
|
156
|
-
write: access.privileged,
|
|
157
|
-
read: access.admin,
|
|
158
|
-
mask: { title: "subscription-dodo.billing-live", icon: "rocket", order: 4 },
|
|
159
|
-
}),
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
r.translations({
|
|
164
|
-
keys: {
|
|
165
|
-
"subscription-dodo.api-key": { en: "Dodo Payments API key" },
|
|
166
|
-
"subscription-dodo.webhook-secret": { en: "Dodo Payments webhook secret" },
|
|
167
|
-
"subscription-dodo.environment": { en: "Dodo Payments environment" },
|
|
168
|
-
"subscription-dodo.billing-live": { en: "Live billing" },
|
|
169
|
-
"subscription-dodo.settings": { en: "Dodo Payments" },
|
|
170
|
-
"screen:subscription-dodo-system.title": { en: "Dodo Payments" },
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
const priceToTier = options.priceToTier ?? {};
|
|
175
|
-
const runtimes = createDodoRuntimes({
|
|
176
|
-
apiKeyHandle: configKeys[DODO_API_KEY_CONFIG],
|
|
177
|
-
webhookSecretHandle: configKeys[DODO_WEBHOOK_SECRET_CONFIG],
|
|
178
|
-
billingLiveHandle: configKeys[DODO_BILLING_LIVE_CONFIG],
|
|
179
|
-
environmentHandle: configKeys[DODO_ENVIRONMENT_CONFIG],
|
|
180
|
-
send: options.send ?? egress({ kind: "external" }),
|
|
181
|
-
fallback: {
|
|
182
|
-
...(options.apiKey !== undefined && { apiKey: options.apiKey }),
|
|
183
|
-
...(options.webhookSecret !== undefined && { webhookSecret: options.webhookSecret }),
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const plugin: SubscriptionProviderPlugin = {
|
|
188
|
-
displayName: "Dodo Payments",
|
|
189
|
-
verifyAndParseWebhook: verifyAndParseDodoWebhook(runtimes.webhook, { priceToTier }),
|
|
190
|
-
createCheckoutSession: createDodoCheckoutSession(runtimes.ctx),
|
|
191
|
-
createPortalSession: createDodoPortalSession(runtimes.ctx),
|
|
192
|
-
cancelSubscription: createDodoCancelSubscription(runtimes.ctx),
|
|
193
|
-
resumeSubscription: createDodoResumeSubscription(runtimes.ctx),
|
|
194
|
-
priceToTier,
|
|
195
|
-
oneOffPriceIds: options.oneOffPriceIds ?? [],
|
|
196
|
-
isBillingEnabled: runtimes.ctx.isBillingEnabled,
|
|
197
|
-
retrievePrices: createDodoRetrievePrices(runtimes.ctx, createDodoPriceCache()),
|
|
198
|
-
retrieveSubscription: createDodoRetrieveSubscription(runtimes.ctx, { priceToTier }),
|
|
199
|
-
previewPlanSwitch: createDodoPreviewPlanSwitch(runtimes.ctx),
|
|
200
|
-
applyPlanSwitch: createDodoApplyPlanSwitch(runtimes.ctx),
|
|
201
|
-
};
|
|
202
|
-
r.useExtension("subscriptionProvider", DODO_PROVIDER_NAME, plugin);
|
|
203
|
-
|
|
204
|
-
return { configKeys };
|
|
205
|
-
});
|
|
206
|
-
return featureWithEnvSchema(feature, subscriptionDodoEnvSchema);
|
|
207
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Public API of the subscription-dodo bundled feature. The webhook verifier,
|
|
2
|
-
// client and plugin methods are internal to feature.ts.
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
DODO_BASE_URLS,
|
|
6
|
-
DODO_PROVIDER_NAME,
|
|
7
|
-
type DodoEnvironment,
|
|
8
|
-
DodoEnvironments,
|
|
9
|
-
SUBSCRIPTION_DODO_FEATURE,
|
|
10
|
-
} from "./constants.ts";
|
|
11
|
-
export {
|
|
12
|
-
createSubscriptionDodoFeature,
|
|
13
|
-
dodoEnvSchema,
|
|
14
|
-
type SubscriptionDodoOptions,
|
|
15
|
-
subscriptionDodoEnvSchema,
|
|
16
|
-
} from "./feature.ts";
|