@cosmicdrift/kumiko-samples 0.338.0 → 0.340.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 +21 -1
- package/packages/bundled-features/src/auth-email-password/changes.json +7 -0
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +2 -6
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +2 -6
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +3 -5
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +2 -8
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +35 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-redirect.ts +60 -0
- package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +10 -0
- package/packages/bundled-features/src/auth-email-password/web/index.ts +8 -0
- package/packages/bundled-features/src/auth-email-password/web/session.tsx +15 -2
- package/packages/bundled-features/src/auth-mfa/feature.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +1 -7
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -5
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -7
- package/packages/bundled-features/src/auth-mfa/mfa-code-verifier.ts +1 -5
- package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +1 -7
- package/packages/bundled-features/src/billing-foundation/changes.json +41 -0
- package/packages/bundled-features/src/billing-foundation/constants.ts +11 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +11 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-text.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +48 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +10 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts +44 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts +32 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +129 -33
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +13 -59
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +20 -21
- package/packages/bundled-features/src/billing-foundation/events.ts +18 -0
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +4 -5
- package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts +6 -5
- package/packages/bundled-features/src/billing-foundation/i18n.ts +3 -0
- package/packages/bundled-features/src/billing-foundation/projection.ts +35 -41
- package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/types.ts +6 -0
- package/packages/bundled-features/src/billing-foundation/validate-options.ts +6 -0
- package/packages/bundled-features/src/channel-discord/discord-channel.ts +32 -0
- package/packages/bundled-features/src/channel-discord/feature.ts +40 -0
- package/packages/bundled-features/src/channel-discord/index.ts +2 -0
- package/packages/bundled-features/src/channel-slack/feature.ts +38 -0
- package/packages/bundled-features/src/channel-slack/index.ts +2 -0
- package/packages/bundled-features/src/channel-slack/slack-channel.ts +29 -0
- package/packages/bundled-features/src/channel-teams/feature.ts +42 -0
- package/packages/bundled-features/src/channel-teams/index.ts +2 -0
- package/packages/bundled-features/src/channel-teams/teams-channel.ts +50 -0
- package/packages/bundled-features/src/channel-telegram/feature.ts +35 -0
- package/packages/bundled-features/src/channel-telegram/index.ts +2 -0
- package/packages/bundled-features/src/channel-telegram/telegram-channel.ts +74 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +6 -18
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +1 -4
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +1 -1
- package/packages/bundled-features/src/data-retention/feature.ts +2 -3
- package/packages/bundled-features/src/delivery/changes.json +7 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +3 -1
- package/packages/bundled-features/src/delivery/chat-webhook-channel.ts +76 -0
- package/packages/bundled-features/src/delivery/chat-webhook-sender.ts +135 -0
- package/packages/bundled-features/src/delivery/delivery-service.ts +3 -0
- package/packages/bundled-features/src/delivery/index.ts +15 -0
- package/packages/bundled-features/src/delivery/jobs.ts +1 -1
- package/packages/bundled-features/src/delivery/testing.ts +2 -0
- package/packages/bundled-features/src/delivery/types.ts +8 -3
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts +1 -1
- package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
- package/packages/bundled-features/src/form-draft/feature.ts +1 -1
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/form-draft/handlers/save.write.ts +1 -5
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +14 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +29 -13
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/index.ts +7 -0
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +201 -0
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +57 -58
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +17 -1
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +74 -36
- package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +1 -1
- package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +7 -10
- package/packages/bundled-features/src/renderer-simple/changes.json +9 -1
- package/packages/bundled-features/src/renderer-simple/feature.ts +14 -4
- package/packages/bundled-features/src/renderer-simple/index.ts +2 -2
- package/packages/bundled-features/src/renderer-simple/simple-renderer.ts +94 -31
- package/packages/bundled-features/src/secrets/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -4
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -3
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +3 -5
- package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts +1 -1
- package/packages/bundled-features/src/tier-engine/feature.ts +1 -1
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +1 -1
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +1 -1
- package/packages/bundled-features/src/user/changes.json +13 -6
- package/packages/bundled-features/src/user/handlers/update-roles.ts +7 -23
- package/packages/bundled-features/src/user-data-rights/feature.ts +4 -6
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -3
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +2 -6
- package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +2 -8
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +14 -0
- package/packages/bundled-features/src/workflow-runner/db/queries/due-runs.ts +10 -13
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +7 -2
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +5 -0
- package/packages/bundled-features/src/workflow-runner/feature.ts +4 -11
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +231 -77
- package/packages/bundled-features/src/workflow-runner/pending-projection.ts +1 -0
- package/packages/bundled-features/src/workflow-runner/runner.ts +12 -2
- package/packages/bundled-features/src/workflow-runner/tables.ts +8 -1
- package/samples/apps/styleguide/src/features/widgets/i18n.ts +5 -0
- package/samples/apps/styleguide/src/features/widgets/web/Widgets.tsx +11 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-constants.ts +16 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +152 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +82 -0
- package/samples/apps/use-all-bundled/feature-manifest.json +146 -1
- package/samples/apps/use-all-bundled/package.json +1 -0
- package/samples/apps/use-all-bundled/playwright.billing-gallery.config.ts +14 -0
- package/samples/apps/use-all-bundled/schema/generate.ts +20 -0
- package/samples/apps/use-all-bundled/scripts/gen-feature-manifest.ts +1 -0
- package/samples/apps/use-all-bundled/src/run-config.ts +10 -0
- package/samples/apps/use-all-bundled/tsconfig.json +1 -0
- package/samples/recipes/chat-channels/README.md +19 -0
- package/samples/recipes/chat-channels/package.json +9 -0
- package/samples/recipes/chat-channels/src/feature.ts +40 -0
- package/samples/recipes/public-share-token/src/feature.ts +1 -1
- package/samples/recipes/workflow-engine/src/event-trigger.ts +5 -0
- package/samples/recipes/workflow-engine/src/postgres-resume-loop.ts +50 -9
- package/samples/recipes/workflow-engine/src/workflow-runner.ts +12 -2
- package/packages/bundled-features/src/billing-foundation/db/queries/payment-projection.ts +0 -19
- package/packages/bundled-features/src/billing-foundation/db/queries/subscription-projection.ts +0 -15
- package/packages/bundled-features/src/inbound-mail-foundation/db/queries/inbound-projections.ts +0 -38
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// @runtime test
|
|
2
|
+
//
|
|
3
|
+
// Framework-free constants shared by the gallery server and spec (the spec
|
|
4
|
+
// runs in a Playwright worker and must not pull the server import chain).
|
|
5
|
+
|
|
6
|
+
export const GALLERY_PROVIDER = "gallery-mock-provider";
|
|
7
|
+
export const GALLERY_TERMS_SLUG = "gallery-terms";
|
|
8
|
+
export const GALLERY_OPERATOR_EMAIL = "billing@gallery.example";
|
|
9
|
+
export const GALLERY_PLAN_PATH = "/tenant-admin/billing-plans";
|
|
10
|
+
|
|
11
|
+
// "localhost" resolves the SYSTEM tenant like the stock dev app; the numeric
|
|
12
|
+
// loopback host resolves none and plays the platform apex.
|
|
13
|
+
export const GALLERY_TENANT_HOST = "localhost";
|
|
14
|
+
export const GALLERY_APEX_HOST = "127.0.0.1";
|
|
15
|
+
|
|
16
|
+
export const GALLERY_PRICE_IDS = { starter: "price_gallery_starter", pro: "price_gallery_pro" };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// @runtime test
|
|
2
|
+
//
|
|
3
|
+
// E2E-only wiring for the consent/termination review gallery: a mock billing
|
|
4
|
+
// provider (the only fake — real handlers, real dispatcher), the catalog and
|
|
5
|
+
// the nav entry that makes billing-foundation's dormant plans screen reachable.
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
createBillingFoundationFeature,
|
|
9
|
+
type ProviderPrice,
|
|
10
|
+
type SubscriptionProviderPlugin,
|
|
11
|
+
SubscriptionStatuses,
|
|
12
|
+
subscriptionAggregateId,
|
|
13
|
+
subscriptionsProjectionTable,
|
|
14
|
+
} from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
15
|
+
import {
|
|
16
|
+
createChannelEmailFeature,
|
|
17
|
+
createInMemoryTransport,
|
|
18
|
+
} from "@cosmicdrift/kumiko-bundled-features/channel-email";
|
|
19
|
+
import { simpleRenderer } from "@cosmicdrift/kumiko-bundled-features/renderer-simple";
|
|
20
|
+
import type { TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
21
|
+
import { defineFeature, type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
22
|
+
import {
|
|
23
|
+
GALLERY_OPERATOR_EMAIL,
|
|
24
|
+
GALLERY_PLAN_PATH,
|
|
25
|
+
GALLERY_PRICE_IDS,
|
|
26
|
+
GALLERY_PROVIDER,
|
|
27
|
+
GALLERY_TERMS_SLUG,
|
|
28
|
+
} from "./gallery-constants";
|
|
29
|
+
|
|
30
|
+
export const galleryMailTransport = createInMemoryTransport();
|
|
31
|
+
|
|
32
|
+
const PRICES: readonly ProviderPrice[] = [
|
|
33
|
+
{
|
|
34
|
+
priceId: GALLERY_PRICE_IDS.starter,
|
|
35
|
+
unitAmount: 900,
|
|
36
|
+
currency: "eur",
|
|
37
|
+
interval: "month",
|
|
38
|
+
intervalCount: 1,
|
|
39
|
+
active: true,
|
|
40
|
+
metadata: {},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
priceId: GALLERY_PRICE_IDS.pro,
|
|
44
|
+
unitAmount: 1900,
|
|
45
|
+
currency: "eur",
|
|
46
|
+
interval: "month",
|
|
47
|
+
intervalCount: 1,
|
|
48
|
+
active: true,
|
|
49
|
+
metadata: {},
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
async function resolveCurrentTier(db: TenantDb, tenantId: string): Promise<string> {
|
|
54
|
+
const rows = await db.selectMany(
|
|
55
|
+
subscriptionsProjectionTable,
|
|
56
|
+
{ id: subscriptionAggregateId(tenantId) },
|
|
57
|
+
{ limit: 1 },
|
|
58
|
+
);
|
|
59
|
+
const row = rows[0];
|
|
60
|
+
if (!row) return "free";
|
|
61
|
+
const status = row["status"] as string;
|
|
62
|
+
return status === SubscriptionStatuses.active || status === SubscriptionStatuses.trialing
|
|
63
|
+
? (row["tier"] as string)
|
|
64
|
+
: "free";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createGalleryBillingFeature(baseUrl: string): FeatureDefinition {
|
|
68
|
+
return createBillingFoundationFeature({
|
|
69
|
+
baseUrl,
|
|
70
|
+
catalog: {
|
|
71
|
+
plans: ["starter", "pro"],
|
|
72
|
+
tierLabelKey: (tier) => `gallery.plan.${tier}.label`,
|
|
73
|
+
benefits: (tier) => [{ labelKey: `gallery.plan.${tier}.benefit` }],
|
|
74
|
+
resolveCurrentTier,
|
|
75
|
+
viewRoles: ["TenantAdmin", "SystemAdmin"],
|
|
76
|
+
successPath: GALLERY_PLAN_PATH,
|
|
77
|
+
cancelPath: GALLERY_PLAN_PATH,
|
|
78
|
+
providerName: GALLERY_PROVIDER,
|
|
79
|
+
},
|
|
80
|
+
consumerProtection: {
|
|
81
|
+
termsTextBlock: GALLERY_TERMS_SLUG,
|
|
82
|
+
vatNote: { de: "Preise inkl. USt.", en: "Prices include VAT." },
|
|
83
|
+
operatorEmail: GALLERY_OPERATOR_EMAIL,
|
|
84
|
+
terminationScope: "platform",
|
|
85
|
+
legalLinks: {
|
|
86
|
+
terms: "/legal/terms",
|
|
87
|
+
withdrawal: "/legal/withdrawal",
|
|
88
|
+
privacy: "/legal/privacy",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function createGalleryEmailFeature(): FeatureDefinition {
|
|
95
|
+
return createChannelEmailFeature({
|
|
96
|
+
transport: galleryMailTransport,
|
|
97
|
+
renderer: simpleRenderer,
|
|
98
|
+
resolveEmail: async () => "unused@gallery.example",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The page URL carries the consent id so the spec can echo it on the webhook,
|
|
103
|
+
// exactly as a real provider echoes the checkout metadata.
|
|
104
|
+
const galleryProviderPlugin: SubscriptionProviderPlugin = {
|
|
105
|
+
verifyAndParseWebhook: async (rawBody) => JSON.parse(rawBody),
|
|
106
|
+
priceToTier: {
|
|
107
|
+
[GALLERY_PRICE_IDS.starter]: "starter",
|
|
108
|
+
[GALLERY_PRICE_IDS.pro]: "pro",
|
|
109
|
+
},
|
|
110
|
+
retrievePrices: async (_ctx, priceIds) => PRICES.filter((p) => priceIds.includes(p.priceId)),
|
|
111
|
+
createCheckoutSession: async (_ctx, options) => {
|
|
112
|
+
const url = new URL(options.successUrl);
|
|
113
|
+
if (options.consentId !== undefined) url.searchParams.set("consentId", options.consentId);
|
|
114
|
+
return { url: url.toString() };
|
|
115
|
+
},
|
|
116
|
+
cancelSubscription: async () => {},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const galleryProviderFeature: FeatureDefinition = defineFeature(
|
|
120
|
+
"gallery-mock-provider",
|
|
121
|
+
(r) => {
|
|
122
|
+
r.describe("E2E-only mock subscription provider for the consent/termination gallery.");
|
|
123
|
+
r.requires("billing-foundation");
|
|
124
|
+
r.useExtension("subscriptionProvider", GALLERY_PROVIDER, galleryProviderPlugin);
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
export const galleryScreensFeature: FeatureDefinition = defineFeature("gallery-screens", (r) => {
|
|
129
|
+
r.describe("E2E-only nav entry for the billing plans screen plus plan labels.");
|
|
130
|
+
r.requires("billing-foundation", "admin-shell");
|
|
131
|
+
r.nav({
|
|
132
|
+
id: "billing",
|
|
133
|
+
label: "gallery:nav.billing",
|
|
134
|
+
icon: "file",
|
|
135
|
+
screen: "billing-foundation:screen:billing-plans",
|
|
136
|
+
order: 20,
|
|
137
|
+
access: { roles: ["TenantAdmin", "SystemAdmin"] },
|
|
138
|
+
workspaces: ["admin-shell:workspace:tenant-admin"],
|
|
139
|
+
});
|
|
140
|
+
r.translations({
|
|
141
|
+
keys: {
|
|
142
|
+
"gallery:nav.billing": { de: "Abrechnung", en: "Billing" },
|
|
143
|
+
"gallery.plan.free.label": { de: "Free", en: "Free" },
|
|
144
|
+
"gallery.plan.starter.label": { de: "Starter", en: "Starter" },
|
|
145
|
+
"gallery.plan.pro.label": { de: "Pro", en: "Pro" },
|
|
146
|
+
"gallery.plan.starter.benefit": { de: "Für kleine Teams", en: "For small teams" },
|
|
147
|
+
"gallery.plan.pro.benefit": { de: "Für wachsende Teams", en: "For growing teams" },
|
|
148
|
+
"gallery.plan.free.benefit": { de: "Zum Ausprobieren", en: "To try things out" },
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
return {};
|
|
152
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// @runtime test
|
|
2
|
+
//
|
|
3
|
+
// Gallery boot: the stock dev app with consumer protection switched on. Only
|
|
4
|
+
// this server mounts the billing catalog, the mock provider, the in-memory
|
|
5
|
+
// mail transport, the webhook/termination routes and a host-based tenant
|
|
6
|
+
// resolver; the shared dev app and its specs stay untouched.
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
createContractTerminationRoutes,
|
|
10
|
+
createSubscriptionWebhookRoute,
|
|
11
|
+
} from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
12
|
+
import { createDeliveryTestContext } from "@cosmicdrift/kumiko-bundled-features/delivery";
|
|
13
|
+
import { createTemplateResolverApi } from "@cosmicdrift/kumiko-bundled-features/template-resolver";
|
|
14
|
+
import { seedTextBlock } from "@cosmicdrift/kumiko-bundled-features/template-resolver/seeding";
|
|
15
|
+
import { runDevApp, type SeedFn } from "@cosmicdrift/kumiko-dev-server";
|
|
16
|
+
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
17
|
+
import { createE2eSeedRoutes } from "@cosmicdrift/kumiko-testing/e2e/seed-route";
|
|
18
|
+
import { devAppOptions } from "../src/app/dev-app-options";
|
|
19
|
+
import { GALLERY_TENANT_HOST, GALLERY_TERMS_SLUG } from "./gallery-constants";
|
|
20
|
+
import {
|
|
21
|
+
createGalleryBillingFeature,
|
|
22
|
+
createGalleryEmailFeature,
|
|
23
|
+
galleryMailTransport,
|
|
24
|
+
galleryProviderFeature,
|
|
25
|
+
galleryScreensFeature,
|
|
26
|
+
} from "./gallery-features";
|
|
27
|
+
|
|
28
|
+
const port = Number.parseInt(process.env["PORT"] ?? "4196", 10);
|
|
29
|
+
|
|
30
|
+
const BILLING_FEATURE = "billing-foundation";
|
|
31
|
+
const EMAIL_FEATURE = "channel-email";
|
|
32
|
+
|
|
33
|
+
const features = [
|
|
34
|
+
...devAppOptions.features.map((feature) => {
|
|
35
|
+
if (feature.name === BILLING_FEATURE)
|
|
36
|
+
return createGalleryBillingFeature(`http://localhost:${port}`);
|
|
37
|
+
if (feature.name === EMAIL_FEATURE) return createGalleryEmailFeature();
|
|
38
|
+
return feature;
|
|
39
|
+
}),
|
|
40
|
+
galleryProviderFeature,
|
|
41
|
+
galleryScreensFeature,
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const TERMS_TEXT: Readonly<Record<string, string>> = {
|
|
45
|
+
de: "## Allgemeine Geschäftsbedingungen\n\nDiese Bedingungen gelten für alle Verträge über die Plattform.",
|
|
46
|
+
en: "## Terms and conditions\n\nThese terms apply to every contract concluded through the platform.",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const seedTerms: SeedFn = async (stack) => {
|
|
50
|
+
for (const [locale, content] of Object.entries(TERMS_TEXT)) {
|
|
51
|
+
await seedTextBlock(stack.db, {
|
|
52
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
53
|
+
slug: GALLERY_TERMS_SLUG,
|
|
54
|
+
locale,
|
|
55
|
+
title: "Terms",
|
|
56
|
+
content,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
await runDevApp({
|
|
62
|
+
...devAppOptions,
|
|
63
|
+
features,
|
|
64
|
+
seeds: [...(devAppOptions.seeds ?? []), seedTerms],
|
|
65
|
+
// The stock dev app pins every host to the SYSTEM tenant, which would hide
|
|
66
|
+
// platform mode: here only the named host resolves a tenant.
|
|
67
|
+
anonymousAccess: {
|
|
68
|
+
tenantResolver: (c) =>
|
|
69
|
+
c.req.header("host")?.startsWith(`${GALLERY_TENANT_HOST}:`) ? SYSTEM_TENANT_ID : null,
|
|
70
|
+
resolverTrust: "authoritative",
|
|
71
|
+
tenantExists: async (id) => id === SYSTEM_TENANT_ID,
|
|
72
|
+
},
|
|
73
|
+
extraContext: (deps) => ({
|
|
74
|
+
...createDeliveryTestContext(deps),
|
|
75
|
+
templateResolver: createTemplateResolverApi(deps.db),
|
|
76
|
+
}),
|
|
77
|
+
extraRoutes: [
|
|
78
|
+
...createE2eSeedRoutes({ mailOutbox: galleryMailTransport }),
|
|
79
|
+
createSubscriptionWebhookRoute(),
|
|
80
|
+
...createContractTerminationRoutes(),
|
|
81
|
+
],
|
|
82
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "samples/apps/use-all-bundled APP_FEATURES (composeFeatures includeBundled + signup)",
|
|
3
|
-
"featureCount":
|
|
3
|
+
"featureCount": 80,
|
|
4
4
|
"features": [
|
|
5
5
|
{
|
|
6
6
|
"name": "admin-shell",
|
|
@@ -352,6 +352,33 @@
|
|
|
352
352
|
"secrets": [],
|
|
353
353
|
"writeHandlers": []
|
|
354
354
|
},
|
|
355
|
+
{
|
|
356
|
+
"name": "channel-discord",
|
|
357
|
+
"description": "Posts delivery notifications to a Discord webhook, registered as the `discord` channel in the delivery system. Tenant-owned chat target: reach it only via `ctx.notify(type, { route: { discord: \"<connection name>\" } })`. The webhook URL is a tenant secret under `channel-discord:webhooks.<connection name>`; only the connection name is logged. Mentions are disabled and content is cut to 2000 characters. Requires `delivery` and `secrets`. Host allowlist (discord.com, discordapp.com, path /api/webhooks/), https-only and the request timeout are app options of `createChannelDiscordFeature(opts)`, never tenant config.",
|
|
358
|
+
"toggleableDefault": null,
|
|
359
|
+
"requires": [
|
|
360
|
+
"delivery",
|
|
361
|
+
"secrets"
|
|
362
|
+
],
|
|
363
|
+
"optionalRequires": [],
|
|
364
|
+
"configReads": [],
|
|
365
|
+
"exposesApis": [],
|
|
366
|
+
"usesApis": [],
|
|
367
|
+
"extensionsUsed": [
|
|
368
|
+
{
|
|
369
|
+
"extensionName": "deliveryChannel",
|
|
370
|
+
"entityName": "discord"
|
|
371
|
+
}
|
|
372
|
+
],
|
|
373
|
+
"configKeys": [],
|
|
374
|
+
"secrets": [],
|
|
375
|
+
"writeHandlers": [],
|
|
376
|
+
"uiHints": {
|
|
377
|
+
"displayLabel": "Discord Channel",
|
|
378
|
+
"category": "notifications",
|
|
379
|
+
"recommended": false
|
|
380
|
+
}
|
|
381
|
+
},
|
|
355
382
|
{
|
|
356
383
|
"name": "channel-email",
|
|
357
384
|
"description": "Wires an `EmailTransport` (typically `mail-transport-smtp` in production, `createInMemoryTransport()` in tests) into the delivery system as the `email` channel. Requires `delivery`; pass an `EmailChannelOptions` with a `transport`, a `renderer: NotificationRenderer` (e.g. backed by `renderer-simple`), and a `resolveEmail` function that maps a user ID to their email address.",
|
|
@@ -433,6 +460,94 @@
|
|
|
433
460
|
"recommended": false
|
|
434
461
|
}
|
|
435
462
|
},
|
|
463
|
+
{
|
|
464
|
+
"name": "channel-slack",
|
|
465
|
+
"description": "Posts delivery notifications to a Slack incoming webhook, registered as the `slack` channel in the delivery system. Tenant-owned chat target: reach it only via `ctx.notify(type, { route: { slack: \"<connection name>\" } })`. The webhook URL is a tenant secret under `channel-slack:webhooks.<connection name>`; only the connection name is logged. Requires `delivery` and `secrets`. Host allowlist (hooks.slack.com), https-only and the request timeout are app options of `createChannelSlackFeature(opts)`, never tenant config.",
|
|
466
|
+
"toggleableDefault": null,
|
|
467
|
+
"requires": [
|
|
468
|
+
"delivery",
|
|
469
|
+
"secrets"
|
|
470
|
+
],
|
|
471
|
+
"optionalRequires": [],
|
|
472
|
+
"configReads": [],
|
|
473
|
+
"exposesApis": [],
|
|
474
|
+
"usesApis": [],
|
|
475
|
+
"extensionsUsed": [
|
|
476
|
+
{
|
|
477
|
+
"extensionName": "deliveryChannel",
|
|
478
|
+
"entityName": "slack"
|
|
479
|
+
}
|
|
480
|
+
],
|
|
481
|
+
"configKeys": [],
|
|
482
|
+
"secrets": [],
|
|
483
|
+
"writeHandlers": [],
|
|
484
|
+
"uiHints": {
|
|
485
|
+
"displayLabel": "Slack Channel",
|
|
486
|
+
"category": "notifications",
|
|
487
|
+
"recommended": false
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
"name": "channel-teams",
|
|
492
|
+
"description": "Posts delivery notifications to a Microsoft Teams webhook (Workflows or connector) as an Adaptive Card, registered as the `teams` channel in the delivery system. Tenant-owned chat target: reach it only via `ctx.notify(type, { route: { teams: \"<connection name>\" } })`. The webhook URL is a tenant secret under `channel-teams:webhooks.<connection name>`; only the connection name is logged. Requires `delivery` and `secrets`. Host allowlist (.webhook.office.com, .logic.azure.com, .powerplatform.com), https-only and the request timeout are app options of `createChannelTeamsFeature(opts)`, never tenant config.",
|
|
493
|
+
"toggleableDefault": null,
|
|
494
|
+
"requires": [
|
|
495
|
+
"delivery",
|
|
496
|
+
"secrets"
|
|
497
|
+
],
|
|
498
|
+
"optionalRequires": [],
|
|
499
|
+
"configReads": [],
|
|
500
|
+
"exposesApis": [],
|
|
501
|
+
"usesApis": [],
|
|
502
|
+
"extensionsUsed": [
|
|
503
|
+
{
|
|
504
|
+
"extensionName": "deliveryChannel",
|
|
505
|
+
"entityName": "teams"
|
|
506
|
+
}
|
|
507
|
+
],
|
|
508
|
+
"configKeys": [],
|
|
509
|
+
"secrets": [],
|
|
510
|
+
"writeHandlers": [],
|
|
511
|
+
"uiHints": {
|
|
512
|
+
"displayLabel": "Microsoft Teams Channel",
|
|
513
|
+
"category": "notifications",
|
|
514
|
+
"recommended": false
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
"name": "channel-telegram",
|
|
519
|
+
"description": "Sends delivery notifications through the Telegram Bot API (`sendMessage`, plain text), registered as the `telegram` channel in the delivery system. Tenant-owned chat target: reach it only via `ctx.notify(type, { route: { telegram: \"<chat id or @channel>\" } })`. The bot token is the tenant secret `channel-telegram:secret:bot-token`; the token never reaches logs or delivery attempts. Requires `delivery` and `secrets`. The API base URL and request timeout are app options of `createChannelTelegramFeature(opts)`, never tenant config.",
|
|
520
|
+
"toggleableDefault": null,
|
|
521
|
+
"requires": [
|
|
522
|
+
"delivery",
|
|
523
|
+
"secrets"
|
|
524
|
+
],
|
|
525
|
+
"optionalRequires": [],
|
|
526
|
+
"configReads": [],
|
|
527
|
+
"exposesApis": [],
|
|
528
|
+
"usesApis": [],
|
|
529
|
+
"extensionsUsed": [
|
|
530
|
+
{
|
|
531
|
+
"extensionName": "deliveryChannel",
|
|
532
|
+
"entityName": "telegram"
|
|
533
|
+
}
|
|
534
|
+
],
|
|
535
|
+
"configKeys": [],
|
|
536
|
+
"secrets": [
|
|
537
|
+
{
|
|
538
|
+
"qualifiedName": "channel-telegram:secret:bot-token",
|
|
539
|
+
"scope": "tenant",
|
|
540
|
+
"label": "Telegram bot token",
|
|
541
|
+
"hint": "Token from @BotFather. Used for every Telegram chat the tenant addresses via route: { telegram: \"<chat id>\" }."
|
|
542
|
+
}
|
|
543
|
+
],
|
|
544
|
+
"writeHandlers": [],
|
|
545
|
+
"uiHints": {
|
|
546
|
+
"displayLabel": "Telegram Channel",
|
|
547
|
+
"category": "notifications",
|
|
548
|
+
"recommended": false
|
|
549
|
+
}
|
|
550
|
+
},
|
|
436
551
|
{
|
|
437
552
|
"name": "collection-labels",
|
|
438
553
|
"description": "Nav labels for the content collections mounted by this sample app.",
|
|
@@ -2959,5 +3074,35 @@
|
|
|
2959
3074
|
"recommended": false
|
|
2960
3075
|
}
|
|
2961
3076
|
}
|
|
3077
|
+
],
|
|
3078
|
+
"systemEvents": [
|
|
3079
|
+
{
|
|
3080
|
+
"name": "kumiko:system:workflow.retry.scheduled",
|
|
3081
|
+
"version": 1
|
|
3082
|
+
},
|
|
3083
|
+
{
|
|
3084
|
+
"name": "kumiko:system:workflow.run-completed",
|
|
3085
|
+
"version": 1
|
|
3086
|
+
},
|
|
3087
|
+
{
|
|
3088
|
+
"name": "kumiko:system:workflow.run-failed",
|
|
3089
|
+
"version": 1
|
|
3090
|
+
},
|
|
3091
|
+
{
|
|
3092
|
+
"name": "kumiko:system:workflow.run-started",
|
|
3093
|
+
"version": 1
|
|
3094
|
+
},
|
|
3095
|
+
{
|
|
3096
|
+
"name": "kumiko:system:workflow.step.resumed",
|
|
3097
|
+
"version": 1
|
|
3098
|
+
},
|
|
3099
|
+
{
|
|
3100
|
+
"name": "kumiko:system:workflow.step.waiting",
|
|
3101
|
+
"version": 1
|
|
3102
|
+
},
|
|
3103
|
+
{
|
|
3104
|
+
"name": "kumiko:system:workflow.step.waiting-for-event",
|
|
3105
|
+
"version": 1
|
|
3106
|
+
}
|
|
2962
3107
|
]
|
|
2963
3108
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"boot:bundled": "KUMIKO_DRY_RUN_ENV=boot bun dist-server/server.js",
|
|
10
10
|
"dev": "PORT=4186 bun --env-file=../../../.env run src/app/server.ts",
|
|
11
11
|
"screenshots": "bun x playwright test e2e/screenshots.spec.ts --config=playwright.config.ts",
|
|
12
|
+
"billing-gallery": "bun x playwright test --config=playwright.billing-gallery.config.ts",
|
|
12
13
|
"e2e": "bun x playwright test e2e/mfa-login.spec.ts --config=playwright.config.ts",
|
|
13
14
|
"gen:manifest": "bun run scripts/gen-feature-manifest.ts",
|
|
14
15
|
"gen:sample-index": "bun run scripts/gen-sample-index.ts"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @runtime test
|
|
2
|
+
// Review gallery for the consent/termination flows. Not part of the CI e2e
|
|
3
|
+
// list (.github/workflows/ci.yml) and gated by SCREENSHOT_DIR like every other
|
|
4
|
+
// screenshot spec: run it by hand, see the `billing-gallery` script.
|
|
5
|
+
import { defineAppE2eConfig, PLAYWRIGHT_DEMO_ENV } from "@cosmicdrift/kumiko-testing/e2e";
|
|
6
|
+
import { E2E_PORTS } from "../../e2e/e2e-ports";
|
|
7
|
+
|
|
8
|
+
export default defineAppE2eConfig({
|
|
9
|
+
port: E2E_PORTS["framework/use-all-bundled-billing-gallery"],
|
|
10
|
+
env: PLAYWRIGHT_DEMO_ENV,
|
|
11
|
+
locale: "de",
|
|
12
|
+
testDir: "./e2e-billing-gallery",
|
|
13
|
+
serverEntry: "e2e-billing-gallery/server.ts",
|
|
14
|
+
});
|
|
@@ -445,6 +445,26 @@ const FEATURE_IMPORT_REGISTRY: Record<string, FeatureImport> = {
|
|
|
445
445
|
path: "@cosmicdrift/kumiko-bundled-features/channel-push",
|
|
446
446
|
factory: "createChannelPushFeature",
|
|
447
447
|
},
|
|
448
|
+
"channel-slack": {
|
|
449
|
+
kind: "factory",
|
|
450
|
+
path: "@cosmicdrift/kumiko-bundled-features/channel-slack",
|
|
451
|
+
factory: "createChannelSlackFeature",
|
|
452
|
+
},
|
|
453
|
+
"channel-discord": {
|
|
454
|
+
kind: "factory",
|
|
455
|
+
path: "@cosmicdrift/kumiko-bundled-features/channel-discord",
|
|
456
|
+
factory: "createChannelDiscordFeature",
|
|
457
|
+
},
|
|
458
|
+
"channel-teams": {
|
|
459
|
+
kind: "factory",
|
|
460
|
+
path: "@cosmicdrift/kumiko-bundled-features/channel-teams",
|
|
461
|
+
factory: "createChannelTeamsFeature",
|
|
462
|
+
},
|
|
463
|
+
"channel-telegram": {
|
|
464
|
+
kind: "factory",
|
|
465
|
+
path: "@cosmicdrift/kumiko-bundled-features/channel-telegram",
|
|
466
|
+
factory: "createChannelTelegramFeature",
|
|
467
|
+
},
|
|
448
468
|
readiness: {
|
|
449
469
|
kind: "named",
|
|
450
470
|
path: "@cosmicdrift/kumiko-bundled-features/readiness",
|
|
@@ -22,6 +22,7 @@ import { authMfaUserDataFeature } from "@cosmicdrift/kumiko-bundled-features/aut
|
|
|
22
22
|
import { billingFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";
|
|
23
23
|
import { capCounterFeature } from "@cosmicdrift/kumiko-bundled-features/cap-counter";
|
|
24
24
|
import { createCapOverviewFeature } from "@cosmicdrift/kumiko-bundled-features/cap-overview";
|
|
25
|
+
import { createChannelDiscordFeature } from "@cosmicdrift/kumiko-bundled-features/channel-discord";
|
|
25
26
|
import {
|
|
26
27
|
createChannelEmailFeature,
|
|
27
28
|
type EmailTransport,
|
|
@@ -31,6 +32,9 @@ import {
|
|
|
31
32
|
createChannelPushFeature,
|
|
32
33
|
type PushTransport,
|
|
33
34
|
} from "@cosmicdrift/kumiko-bundled-features/channel-push";
|
|
35
|
+
import { createChannelSlackFeature } from "@cosmicdrift/kumiko-bundled-features/channel-slack";
|
|
36
|
+
import { createChannelTeamsFeature } from "@cosmicdrift/kumiko-bundled-features/channel-teams";
|
|
37
|
+
import { createChannelTelegramFeature } from "@cosmicdrift/kumiko-bundled-features/channel-telegram";
|
|
34
38
|
import { createComplianceProfilesFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles";
|
|
35
39
|
import { complianceProfilesOpsFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles-ops";
|
|
36
40
|
import { customFieldsFeature } from "@cosmicdrift/kumiko-bundled-features/custom-fields";
|
|
@@ -171,6 +175,12 @@ export const APP_FEATURES = [
|
|
|
171
175
|
transport: stubPushTransport,
|
|
172
176
|
resolveToken: async () => "smoke-push-token",
|
|
173
177
|
}),
|
|
178
|
+
// Chat channels are route-only and read their webhook URL / bot token from the
|
|
179
|
+
// tenant's secrets at send time, so the smoke app needs no credentials here.
|
|
180
|
+
createChannelSlackFeature(),
|
|
181
|
+
createChannelDiscordFeature(),
|
|
182
|
+
createChannelTeamsFeature(),
|
|
183
|
+
createChannelTelegramFeature(),
|
|
174
184
|
|
|
175
185
|
// mail (foundation before transport)
|
|
176
186
|
mailFoundationFeature,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Sample: Chat Channels
|
|
2
|
+
|
|
3
|
+
Shows how a feature posts to a tenant's Slack channel without ever handling the webhook URL.
|
|
4
|
+
|
|
5
|
+
## What you learn here
|
|
6
|
+
|
|
7
|
+
**Route-only targets:** Chat targets belong to the tenant, not to a user. A handler addresses one with `ctx.notify(type, { route: { slack: "ops" } })`; `"ops"` is a connection name. The chat channels declare no `resolve`, so a normal `to: userId` notification skips them without a log row.
|
|
8
|
+
|
|
9
|
+
**Secrets for the URL:** The tenant stores the webhook URL as a secret under `channel-slack:webhooks.<connection name>` (namespace declared by the channel, written through `secrets:write:set`). The `delivery.send` job reads it with an audit entry (`channel-slack:send`), posts the message and records only the connection name and an outcome code (`http_500`, `timeout`, `redirect_blocked`, `host_not_allowed`, `missing_credentials`, `invalid_address`).
|
|
10
|
+
|
|
11
|
+
**App-level hardening:** Each channel only talks to its provider's hosts, over https, without following redirects. `allowedHosts`, `requireHttps` and `timeoutMs` are options of `createChannelSlackFeature(opts)`. Tenants cannot change them; the test uses them to point at a local stub.
|
|
12
|
+
|
|
13
|
+
The same shape works for `channel-discord`, `channel-teams` (connection name + webhook secret) and `channel-telegram` (one `botToken` secret, the address is the chat id).
|
|
14
|
+
|
|
15
|
+
## Run
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun test --config=bunfig.integration.toml --timeout=15000 samples/recipes/chat-channels
|
|
19
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cosmicdrift/kumiko-sample-chat-channels",
|
|
3
|
+
"description": "Chat channels. Delivery notifications to Slack, minus the browser tab.",
|
|
4
|
+
"private": true,
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@cosmicdrift/kumiko-bundled-features": "workspace:*",
|
|
7
|
+
"@cosmicdrift/kumiko-framework": "workspace:*"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Chat Channels Sample
|
|
2
|
+
//
|
|
3
|
+
// A handler tells the tenant's ops chat about something. Chat targets belong to
|
|
4
|
+
// the tenant, not to a user, so the feature addresses one by connection name via
|
|
5
|
+
// `route` — it never sees the webhook URL. The tenant stores that URL as a secret
|
|
6
|
+
// under `channel-slack:webhooks.<connection name>`.
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
defineFeature,
|
|
10
|
+
defineWriteHandler,
|
|
11
|
+
type NotifyFn,
|
|
12
|
+
qn,
|
|
13
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
14
|
+
import * as z from "zod";
|
|
15
|
+
|
|
16
|
+
export const OPS_ANNOUNCEMENT_TYPE = qn("ops", "notify", "announcement");
|
|
17
|
+
|
|
18
|
+
export const opsFeature = defineFeature("ops", (r) => {
|
|
19
|
+
r.requires("delivery");
|
|
20
|
+
|
|
21
|
+
r.writeHandler(
|
|
22
|
+
defineWriteHandler({
|
|
23
|
+
name: "announce",
|
|
24
|
+
schema: z.object({
|
|
25
|
+
connection: z.string(),
|
|
26
|
+
title: z.string().min(1),
|
|
27
|
+
body: z.string().optional(),
|
|
28
|
+
}),
|
|
29
|
+
access: { roles: ["Admin"] },
|
|
30
|
+
handler: async (event, ctx) => {
|
|
31
|
+
const notify = ctx.notify as NotifyFn;
|
|
32
|
+
await notify(OPS_ANNOUNCEMENT_TYPE, {
|
|
33
|
+
route: { slack: event.payload.connection },
|
|
34
|
+
data: { title: event.payload.title, body: event.payload.body },
|
|
35
|
+
});
|
|
36
|
+
return { isSuccess: true, data: { announced: true } };
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
});
|
|
@@ -169,7 +169,7 @@ export const shareByTokenQuery = defineQueryHandler({
|
|
|
169
169
|
},
|
|
170
170
|
handler: async (query, ctx) => {
|
|
171
171
|
const hash = await hashToken(query.payload.token);
|
|
172
|
-
const row = await fetchOne<ShareLinkRow>(ctx.db.unsafeRaw(
|
|
172
|
+
const row = await fetchOne<ShareLinkRow>(ctx.db.unsafeRaw(), table, {
|
|
173
173
|
tokenHash: hash,
|
|
174
174
|
});
|
|
175
175
|
|
|
@@ -60,6 +60,11 @@ export function registerEventTrigger(r: FeatureRegistrar, workflow: WorkflowDefi
|
|
|
60
60
|
runId,
|
|
61
61
|
workflow,
|
|
62
62
|
triggerEvent,
|
|
63
|
+
triggerEventRef: {
|
|
64
|
+
eventId: event.id,
|
|
65
|
+
aggregateId: event.aggregateId,
|
|
66
|
+
version: event.version,
|
|
67
|
+
},
|
|
63
68
|
...(idempotencyKey && { idempotencyKey }),
|
|
64
69
|
handlerCtx: ctx as never,
|
|
65
70
|
});
|