@digimuza/saas-core 0.0.2
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/CLAUDE.md +1165 -0
- package/LICENSE +21 -0
- package/README.md +234 -0
- package/dist/api-keys/index.d.ts +9 -0
- package/dist/api-keys/index.d.ts.map +1 -0
- package/dist/api-keys/index.js +37 -0
- package/dist/api-keys/index.js.map +1 -0
- package/dist/api-keys/keys.d.ts +55 -0
- package/dist/api-keys/keys.d.ts.map +1 -0
- package/dist/api-keys/keys.js +92 -0
- package/dist/api-keys/keys.js.map +1 -0
- package/dist/api-keys/service.d.ts +111 -0
- package/dist/api-keys/service.d.ts.map +1 -0
- package/dist/api-keys/service.js +210 -0
- package/dist/api-keys/service.js.map +1 -0
- package/dist/audit/index.d.ts +5 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +44 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/service.d.ts +88 -0
- package/dist/audit/service.d.ts.map +1 -0
- package/dist/audit/service.js +128 -0
- package/dist/audit/service.js.map +1 -0
- package/dist/auth/client.d.ts +5 -0
- package/dist/auth/client.d.ts.map +1 -0
- package/dist/auth/client.js +26 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/cookies.d.ts +51 -0
- package/dist/auth/cookies.d.ts.map +1 -0
- package/dist/auth/cookies.js +58 -0
- package/dist/auth/cookies.js.map +1 -0
- package/dist/auth/index.d.ts +14 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +46 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/service.d.ts +67 -0
- package/dist/auth/service.d.ts.map +1 -0
- package/dist/auth/service.js +275 -0
- package/dist/auth/service.js.map +1 -0
- package/dist/auth/types.d.ts +56 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/authorization/index.d.ts +4 -0
- package/dist/authorization/index.d.ts.map +1 -0
- package/dist/authorization/index.js +24 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/authorization/service.d.ts +92 -0
- package/dist/authorization/service.d.ts.map +1 -0
- package/dist/authorization/service.js +254 -0
- package/dist/authorization/service.js.map +1 -0
- package/dist/billing/index.d.ts +16 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +47 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/provider.d.ts +114 -0
- package/dist/billing/provider.d.ts.map +1 -0
- package/dist/billing/provider.js +2 -0
- package/dist/billing/provider.js.map +1 -0
- package/dist/billing/revolut.d.ts +68 -0
- package/dist/billing/revolut.d.ts.map +1 -0
- package/dist/billing/revolut.js +181 -0
- package/dist/billing/revolut.js.map +1 -0
- package/dist/billing/service.d.ts +124 -0
- package/dist/billing/service.d.ts.map +1 -0
- package/dist/billing/service.js +470 -0
- package/dist/billing/service.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +43 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +212 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/project.d.ts +12 -0
- package/dist/cli/commands/project.d.ts.map +1 -0
- package/dist/cli/commands/project.js +49 -0
- package/dist/cli/commands/project.js.map +1 -0
- package/dist/cli/docker.d.ts +50 -0
- package/dist/cli/docker.d.ts.map +1 -0
- package/dist/cli/docker.js +84 -0
- package/dist/cli/docker.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +198 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +30 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +66 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/templates.d.ts +52 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +225 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/config/client.d.ts +60 -0
- package/dist/config/client.d.ts.map +1 -0
- package/dist/config/client.js +94 -0
- package/dist/config/client.js.map +1 -0
- package/dist/config/config-loader.d.ts +2 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +2 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config/env.d.ts +24 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +40 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/errors.d.ts +17 -0
- package/dist/config/errors.d.ts.map +1 -0
- package/dist/config/errors.js +26 -0
- package/dist/config/errors.js.map +1 -0
- package/dist/config/index.d.ts +72 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +106 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/runtime.d.ts +15 -0
- package/dist/config/runtime.d.ts.map +1 -0
- package/dist/config/runtime.js +22 -0
- package/dist/config/runtime.js.map +1 -0
- package/dist/config/sections.d.ts +226 -0
- package/dist/config/sections.d.ts.map +1 -0
- package/dist/config/sections.js +141 -0
- package/dist/config/sections.js.map +1 -0
- package/dist/db/client.d.ts +43 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +77 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/errors.d.ts +39 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +67 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +10 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate.d.ts +21 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +28 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema/_shared.d.ts +30 -0
- package/dist/db/schema/_shared.d.ts.map +1 -0
- package/dist/db/schema/_shared.js +36 -0
- package/dist/db/schema/_shared.js.map +1 -0
- package/dist/db/schema/apikeys.d.ts +263 -0
- package/dist/db/schema/apikeys.d.ts.map +1 -0
- package/dist/db/schema/apikeys.js +70 -0
- package/dist/db/schema/apikeys.js.map +1 -0
- package/dist/db/schema/authorization.d.ts +465 -0
- package/dist/db/schema/authorization.d.ts.map +1 -0
- package/dist/db/schema/authorization.js +134 -0
- package/dist/db/schema/authorization.js.map +1 -0
- package/dist/db/schema/billing.d.ts +1100 -0
- package/dist/db/schema/billing.d.ts.map +1 -0
- package/dist/db/schema/billing.js +223 -0
- package/dist/db/schema/billing.js.map +1 -0
- package/dist/db/schema/events.d.ts +161 -0
- package/dist/db/schema/events.d.ts.map +1 -0
- package/dist/db/schema/events.js +66 -0
- package/dist/db/schema/events.js.map +1 -0
- package/dist/db/schema/features.d.ts +243 -0
- package/dist/db/schema/features.d.ts.map +1 -0
- package/dist/db/schema/features.js +87 -0
- package/dist/db/schema/features.js.map +1 -0
- package/dist/db/schema/files.d.ts +253 -0
- package/dist/db/schema/files.d.ts.map +1 -0
- package/dist/db/schema/files.js +74 -0
- package/dist/db/schema/files.js.map +1 -0
- package/dist/db/schema/index.d.ts +38 -0
- package/dist/db/schema/index.d.ts.map +1 -0
- package/dist/db/schema/index.js +38 -0
- package/dist/db/schema/index.js.map +1 -0
- package/dist/db/schema/invitations.d.ts +214 -0
- package/dist/db/schema/invitations.d.ts.map +1 -0
- package/dist/db/schema/invitations.js +80 -0
- package/dist/db/schema/invitations.js.map +1 -0
- package/dist/db/schema/messaging.d.ts +442 -0
- package/dist/db/schema/messaging.d.ts.map +1 -0
- package/dist/db/schema/messaging.js +120 -0
- package/dist/db/schema/messaging.js.map +1 -0
- package/dist/db/schema/users.d.ts +246 -0
- package/dist/db/schema/users.d.ts.map +1 -0
- package/dist/db/schema/users.js +60 -0
- package/dist/db/schema/users.js.map +1 -0
- package/dist/db/schema/webhooks.d.ts +693 -0
- package/dist/db/schema/webhooks.d.ts.map +1 -0
- package/dist/db/schema/webhooks.js +147 -0
- package/dist/db/schema/webhooks.js.map +1 -0
- package/dist/db/schema/workspaces.d.ts +196 -0
- package/dist/db/schema/workspaces.d.ts.map +1 -0
- package/dist/db/schema/workspaces.js +57 -0
- package/dist/db/schema/workspaces.js.map +1 -0
- package/dist/db/types.d.ts +18 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/email/adapters.d.ts +29 -0
- package/dist/email/adapters.d.ts.map +1 -0
- package/dist/email/adapters.js +60 -0
- package/dist/email/adapters.js.map +1 -0
- package/dist/email/index.d.ts +20 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +79 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/provider.d.ts +47 -0
- package/dist/email/provider.d.ts.map +1 -0
- package/dist/email/provider.js +7 -0
- package/dist/email/provider.js.map +1 -0
- package/dist/email/resend.d.ts +22 -0
- package/dist/email/resend.d.ts.map +1 -0
- package/dist/email/resend.js +51 -0
- package/dist/email/resend.js.map +1 -0
- package/dist/email/service.d.ts +55 -0
- package/dist/email/service.d.ts.map +1 -0
- package/dist/email/service.js +87 -0
- package/dist/email/service.js.map +1 -0
- package/dist/email/templates.d.ts +81 -0
- package/dist/email/templates.d.ts.map +1 -0
- package/dist/email/templates.js +84 -0
- package/dist/email/templates.js.map +1 -0
- package/dist/entitlements/index.d.ts +10 -0
- package/dist/entitlements/index.d.ts.map +1 -0
- package/dist/entitlements/index.js +28 -0
- package/dist/entitlements/index.js.map +1 -0
- package/dist/entitlements/service.d.ts +83 -0
- package/dist/entitlements/service.d.ts.map +1 -0
- package/dist/entitlements/service.js +158 -0
- package/dist/entitlements/service.js.map +1 -0
- package/dist/errors.d.ts +85 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +95 -0
- package/dist/errors.js.map +1 -0
- package/dist/events/core-events.d.ts +120 -0
- package/dist/events/core-events.d.ts.map +1 -0
- package/dist/events/core-events.js +109 -0
- package/dist/events/core-events.js.map +1 -0
- package/dist/events/index.d.ts +15 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +58 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/publisher.d.ts +46 -0
- package/dist/events/publisher.d.ts.map +1 -0
- package/dist/events/publisher.js +54 -0
- package/dist/events/publisher.js.map +1 -0
- package/dist/events/registry.d.ts +87 -0
- package/dist/events/registry.d.ts.map +1 -0
- package/dist/events/registry.js +34 -0
- package/dist/events/registry.js.map +1 -0
- package/dist/events/service.d.ts +78 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/service.js +182 -0
- package/dist/events/service.js.map +1 -0
- package/dist/features/bucket.d.ts +22 -0
- package/dist/features/bucket.d.ts.map +1 -0
- package/dist/features/bucket.js +42 -0
- package/dist/features/bucket.js.map +1 -0
- package/dist/features/index.d.ts +15 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +78 -0
- package/dist/features/index.js.map +1 -0
- package/dist/features/registry.d.ts +51 -0
- package/dist/features/registry.d.ts.map +1 -0
- package/dist/features/registry.js +52 -0
- package/dist/features/registry.js.map +1 -0
- package/dist/features/service.d.ts +78 -0
- package/dist/features/service.d.ts.map +1 -0
- package/dist/features/service.js +207 -0
- package/dist/features/service.js.map +1 -0
- package/dist/files/index.d.ts +6 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +64 -0
- package/dist/files/index.js.map +1 -0
- package/dist/files/service.d.ts +92 -0
- package/dist/files/service.d.ts.map +1 -0
- package/dist/files/service.js +303 -0
- package/dist/files/service.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs/backend.d.ts +23 -0
- package/dist/jobs/backend.d.ts.map +1 -0
- package/dist/jobs/backend.js +23 -0
- package/dist/jobs/backend.js.map +1 -0
- package/dist/jobs/index.d.ts +29 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +114 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/service.d.ts +67 -0
- package/dist/jobs/service.d.ts.map +1 -0
- package/dist/jobs/service.js +139 -0
- package/dist/jobs/service.js.map +1 -0
- package/dist/jobs/types.d.ts +78 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +4 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/logger/default.d.ts +7 -0
- package/dist/logger/default.d.ts.map +1 -0
- package/dist/logger/default.js +28 -0
- package/dist/logger/default.js.map +1 -0
- package/dist/logger/index.d.ts +24 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +90 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/redact.d.ts +40 -0
- package/dist/logger/redact.d.ts.map +1 -0
- package/dist/logger/redact.js +178 -0
- package/dist/logger/redact.js.map +1 -0
- package/dist/logger/service.d.ts +14 -0
- package/dist/logger/service.d.ts.map +1 -0
- package/dist/logger/service.js +56 -0
- package/dist/logger/service.js.map +1 -0
- package/dist/logger/sinks.d.ts +37 -0
- package/dist/logger/sinks.d.ts.map +1 -0
- package/dist/logger/sinks.js +135 -0
- package/dist/logger/sinks.js.map +1 -0
- package/dist/logger/types.d.ts +47 -0
- package/dist/logger/types.d.ts.map +1 -0
- package/dist/logger/types.js +8 -0
- package/dist/logger/types.js.map +1 -0
- package/dist/notifications/index.d.ts +8 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +72 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/service.d.ts +79 -0
- package/dist/notifications/service.d.ts.map +1 -0
- package/dist/notifications/service.js +164 -0
- package/dist/notifications/service.js.map +1 -0
- package/dist/realtime/channels.d.ts +37 -0
- package/dist/realtime/channels.d.ts.map +1 -0
- package/dist/realtime/channels.js +58 -0
- package/dist/realtime/channels.js.map +1 -0
- package/dist/realtime/index.d.ts +19 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/index.js +118 -0
- package/dist/realtime/index.js.map +1 -0
- package/dist/realtime/postgres.d.ts +48 -0
- package/dist/realtime/postgres.d.ts.map +1 -0
- package/dist/realtime/postgres.js +151 -0
- package/dist/realtime/postgres.js.map +1 -0
- package/dist/realtime/service.d.ts +75 -0
- package/dist/realtime/service.d.ts.map +1 -0
- package/dist/realtime/service.js +103 -0
- package/dist/realtime/service.js.map +1 -0
- package/dist/realtime/sse.d.ts +55 -0
- package/dist/realtime/sse.d.ts.map +1 -0
- package/dist/realtime/sse.js +123 -0
- package/dist/realtime/sse.js.map +1 -0
- package/dist/realtime/transport.d.ts +52 -0
- package/dist/realtime/transport.d.ts.map +1 -0
- package/dist/realtime/transport.js +2 -0
- package/dist/realtime/transport.js.map +1 -0
- package/dist/storage/index.d.ts +18 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +89 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/keys.d.ts +50 -0
- package/dist/storage/keys.d.ts.map +1 -0
- package/dist/storage/keys.js +120 -0
- package/dist/storage/keys.js.map +1 -0
- package/dist/storage/memory.d.ts +32 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +84 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/provider.d.ts +93 -0
- package/dist/storage/provider.d.ts.map +1 -0
- package/dist/storage/provider.js +2 -0
- package/dist/storage/provider.js.map +1 -0
- package/dist/storage/s3.d.ts +34 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +175 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/storage/service.d.ts +86 -0
- package/dist/storage/service.d.ts.map +1 -0
- package/dist/storage/service.js +107 -0
- package/dist/storage/service.js.map +1 -0
- package/dist/storage/sigv4.d.ts +58 -0
- package/dist/storage/sigv4.d.ts.map +1 -0
- package/dist/storage/sigv4.js +114 -0
- package/dist/storage/sigv4.js.map +1 -0
- package/dist/streams/client.d.ts +91 -0
- package/dist/streams/client.d.ts.map +1 -0
- package/dist/streams/client.js +128 -0
- package/dist/streams/client.js.map +1 -0
- package/dist/streams/events.d.ts +68 -0
- package/dist/streams/events.d.ts.map +1 -0
- package/dist/streams/events.js +81 -0
- package/dist/streams/events.js.map +1 -0
- package/dist/streams/index.d.ts +47 -0
- package/dist/streams/index.d.ts.map +1 -0
- package/dist/streams/index.js +47 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/realtime.d.ts +27 -0
- package/dist/streams/realtime.d.ts.map +1 -0
- package/dist/streams/realtime.js +50 -0
- package/dist/streams/realtime.js.map +1 -0
- package/dist/streams/state.d.ts +26 -0
- package/dist/streams/state.d.ts.map +1 -0
- package/dist/streams/state.js +34 -0
- package/dist/streams/state.js.map +1 -0
- package/dist/users/index.d.ts +9 -0
- package/dist/users/index.d.ts.map +1 -0
- package/dist/users/index.js +23 -0
- package/dist/users/index.js.map +1 -0
- package/dist/users/service.d.ts +43 -0
- package/dist/users/service.d.ts.map +1 -0
- package/dist/users/service.js +113 -0
- package/dist/users/service.js.map +1 -0
- package/dist/webhooks/inbound.d.ts +70 -0
- package/dist/webhooks/inbound.d.ts.map +1 -0
- package/dist/webhooks/inbound.js +146 -0
- package/dist/webhooks/inbound.js.map +1 -0
- package/dist/webhooks/index.d.ts +12 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +65 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/outgoing.d.ts +89 -0
- package/dist/webhooks/outgoing.d.ts.map +1 -0
- package/dist/webhooks/outgoing.js +375 -0
- package/dist/webhooks/outgoing.js.map +1 -0
- package/dist/webhooks/signature.d.ts +40 -0
- package/dist/webhooks/signature.d.ts.map +1 -0
- package/dist/webhooks/signature.js +77 -0
- package/dist/webhooks/signature.js.map +1 -0
- package/dist/workspaces/context.d.ts +30 -0
- package/dist/workspaces/context.d.ts.map +1 -0
- package/dist/workspaces/context.js +12 -0
- package/dist/workspaces/context.js.map +1 -0
- package/dist/workspaces/index.d.ts +5 -0
- package/dist/workspaces/index.d.ts.map +1 -0
- package/dist/workspaces/index.js +21 -0
- package/dist/workspaces/index.js.map +1 -0
- package/dist/workspaces/service.d.ts +126 -0
- package/dist/workspaces/service.d.ts.map +1 -0
- package/dist/workspaces/service.js +495 -0
- package/dist/workspaces/service.js.map +1 -0
- package/docs/README.md +71 -0
- package/docs/api-keys.md +184 -0
- package/docs/audit.md +198 -0
- package/docs/auth.md +196 -0
- package/docs/authorization.md +225 -0
- package/docs/billing.md +293 -0
- package/docs/cli.md +238 -0
- package/docs/config.md +163 -0
- package/docs/db.md +219 -0
- package/docs/email.md +222 -0
- package/docs/entitlements.md +194 -0
- package/docs/errors.md +158 -0
- package/docs/events.md +279 -0
- package/docs/feature-flags.md +237 -0
- package/docs/files.md +229 -0
- package/docs/jobs.md +288 -0
- package/docs/logger.md +269 -0
- package/docs/notifications.md +179 -0
- package/docs/publishing.md +128 -0
- package/docs/realtime.md +360 -0
- package/docs/storage.md +279 -0
- package/docs/streams.md +296 -0
- package/docs/users.md +117 -0
- package/docs/webhooks.md +272 -0
- package/docs/workspaces.md +269 -0
- package/drizzle/0000_init_users.sql +21 -0
- package/drizzle/0001_workspaces_roles_permissions.sql +86 -0
- package/drizzle/0002_billing_entitlements.sql +97 -0
- package/drizzle/0003_events.sql +17 -0
- package/drizzle/0004_notifications_audit.sql +64 -0
- package/drizzle/0005_api_keys_webhooks.sql +77 -0
- package/drizzle/0006_feature_flags.sql +22 -0
- package/drizzle/0007_files.sql +23 -0
- package/drizzle/meta/0000_snapshot.json +160 -0
- package/drizzle/meta/0001_snapshot.json +837 -0
- package/drizzle/meta/0002_snapshot.json +1580 -0
- package/drizzle/meta/0003_snapshot.json +1751 -0
- package/drizzle/meta/0004_snapshot.json +2109 -0
- package/drizzle/meta/0005_snapshot.json +2719 -0
- package/drizzle/meta/0006_snapshot.json +2909 -0
- package/drizzle/meta/0007_snapshot.json +3113 -0
- package/drizzle/meta/_journal.json +62 -0
- package/package.json +180 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Client-side realtime, as an Observable over the SSE endpoint.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately standalone: this module imports nothing from the server half of
|
|
6
|
+
* the package — no config, no database, no transport. It runs in a browser, and a
|
|
7
|
+
* shared import would drag server code into the bundle.
|
|
8
|
+
*
|
|
9
|
+
* It is also the *shared* abstraction the architecture calls for. Every component
|
|
10
|
+
* piping this one stream beats each of them opening its own `EventSource`: one
|
|
11
|
+
* connection, one reconnect policy, one place to reason about ordering.
|
|
12
|
+
*/
|
|
13
|
+
/** The subset of `EventSource` this module uses. */
|
|
14
|
+
export interface EventSourceLike {
|
|
15
|
+
addEventListener(type: string, listener: (event: MessageEventLike) => void): void;
|
|
16
|
+
close(): void;
|
|
17
|
+
readonly readyState?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface MessageEventLike {
|
|
20
|
+
readonly data?: unknown;
|
|
21
|
+
readonly lastEventId?: string;
|
|
22
|
+
}
|
|
23
|
+
/** A realtime message as it arrives in the browser. */
|
|
24
|
+
export interface ClientRealtimeMessage {
|
|
25
|
+
readonly channel: string;
|
|
26
|
+
readonly eventId: string;
|
|
27
|
+
readonly type: string;
|
|
28
|
+
readonly occurredAt: string;
|
|
29
|
+
readonly data?: unknown;
|
|
30
|
+
/** True when the payload was too large to carry; re-read the event by id. */
|
|
31
|
+
readonly truncated?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface EventStreamSourceOptions {
|
|
34
|
+
/** Endpoint serving the stream, e.g. `/api/realtime`. */
|
|
35
|
+
readonly url: string;
|
|
36
|
+
/** Channels to request. Appended as repeated `channel` query parameters. */
|
|
37
|
+
readonly channels: readonly string[];
|
|
38
|
+
/**
|
|
39
|
+
* Event types to listen for.
|
|
40
|
+
*
|
|
41
|
+
* Required because SSE dispatches by event name, and the server names each
|
|
42
|
+
* frame after the event type. Listening for `message` alone would receive
|
|
43
|
+
* nothing.
|
|
44
|
+
*/
|
|
45
|
+
readonly types: readonly string[];
|
|
46
|
+
/**
|
|
47
|
+
* Construct the underlying source. Defaults to the global `EventSource`.
|
|
48
|
+
* Injectable so reconnect behaviour is testable without a browser.
|
|
49
|
+
*/
|
|
50
|
+
readonly createSource?: (url: string) => EventSourceLike;
|
|
51
|
+
/** First reconnect delay, doubling up to {@link maxRetryDelayMs}. */
|
|
52
|
+
readonly initialRetryDelayMs?: number;
|
|
53
|
+
readonly maxRetryDelayMs?: number;
|
|
54
|
+
/** Called on every transport error, before the reconnect is scheduled. */
|
|
55
|
+
readonly onError?: (error: unknown) => void;
|
|
56
|
+
/** Injectable clock, so tests need no real waiting. */
|
|
57
|
+
readonly setTimeoutImpl?: (handler: () => void, ms: number) => unknown;
|
|
58
|
+
readonly clearTimeoutImpl?: (handle: unknown) => void;
|
|
59
|
+
}
|
|
60
|
+
export declare function buildStreamUrl(url: string, channels: readonly string[]): string;
|
|
61
|
+
/**
|
|
62
|
+
* Subscribe to the server's SSE endpoint, reconnecting with exponential backoff.
|
|
63
|
+
*
|
|
64
|
+
* ```ts
|
|
65
|
+
* const events$ = fromEventStream({
|
|
66
|
+
* url: "/api/realtime",
|
|
67
|
+
* channels: [`workspace:${workspaceId}`],
|
|
68
|
+
* types: ["project.created", "job.progress"],
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* const sub = events$.pipe(
|
|
72
|
+
* filter((message) => message.type === "job.progress"),
|
|
73
|
+
* map((message) => message.data as { percent?: number }),
|
|
74
|
+
* distinctUntilChanged((a, b) => a.percent === b.percent),
|
|
75
|
+
* ).subscribe(render);
|
|
76
|
+
*
|
|
77
|
+
* // Every subscription needs a lifecycle. In React, this is the effect cleanup.
|
|
78
|
+
* return () => sub.unsubscribe();
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* The Observable never completes on its own — a realtime stream has no natural
|
|
82
|
+
* end. It stays open until unsubscribed, so bound it with `takeUntil` if
|
|
83
|
+
* something else should end it.
|
|
84
|
+
*
|
|
85
|
+
* Errors do **not** terminate the stream. A dropped connection is expected, not
|
|
86
|
+
* exceptional: it is reported through `onError` and then retried, because
|
|
87
|
+
* surfacing it as an RxJS error would tear down every downstream operator and
|
|
88
|
+
* lose the subscriber's accumulated state on a transient blip.
|
|
89
|
+
*/
|
|
90
|
+
export declare function fromEventStream(options: EventStreamSourceOptions): Observable<ClientRealtimeMessage>;
|
|
91
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/streams/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAiB,MAAM,MAAM,CAAC;AAEjD;;;;;;;;;;GAUG;AAEH,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI,CAAC;IAClF,KAAK,IAAI,IAAI,CAAC;IACd,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,uDAAuD;AACvD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,yDAAyD;IACzD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,eAAe,CAAC;IACzD,qEAAqE;IACrE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,uDAAuD;IACvD,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACvE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACvD;AAKD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAQ/E;AAyBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,wBAAwB,GAChC,UAAU,CAAC,qBAAqB,CAAC,CAgFnC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
const DEFAULT_INITIAL_RETRY_MS = 1_000;
|
|
3
|
+
const DEFAULT_MAX_RETRY_MS = 30_000;
|
|
4
|
+
export function buildStreamUrl(url, channels) {
|
|
5
|
+
// Relative endpoints are normal in a browser, so a base is supplied and then
|
|
6
|
+
// discarded rather than requiring an absolute URL.
|
|
7
|
+
const parsed = new URL(url, "http://local");
|
|
8
|
+
for (const channel of channels) {
|
|
9
|
+
parsed.searchParams.append("channel", channel);
|
|
10
|
+
}
|
|
11
|
+
return url.startsWith("http") ? parsed.toString() : `${parsed.pathname}${parsed.search}`;
|
|
12
|
+
}
|
|
13
|
+
function parseMessage(raw) {
|
|
14
|
+
if (typeof raw !== "string") {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(raw);
|
|
19
|
+
// Validated, not cast: this crossed the network.
|
|
20
|
+
if (typeof parsed.channel !== "string" ||
|
|
21
|
+
typeof parsed.eventId !== "string" ||
|
|
22
|
+
typeof parsed.type !== "string" ||
|
|
23
|
+
typeof parsed.occurredAt !== "string") {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Subscribe to the server's SSE endpoint, reconnecting with exponential backoff.
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* const events$ = fromEventStream({
|
|
37
|
+
* url: "/api/realtime",
|
|
38
|
+
* channels: [`workspace:${workspaceId}`],
|
|
39
|
+
* types: ["project.created", "job.progress"],
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* const sub = events$.pipe(
|
|
43
|
+
* filter((message) => message.type === "job.progress"),
|
|
44
|
+
* map((message) => message.data as { percent?: number }),
|
|
45
|
+
* distinctUntilChanged((a, b) => a.percent === b.percent),
|
|
46
|
+
* ).subscribe(render);
|
|
47
|
+
*
|
|
48
|
+
* // Every subscription needs a lifecycle. In React, this is the effect cleanup.
|
|
49
|
+
* return () => sub.unsubscribe();
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* The Observable never completes on its own — a realtime stream has no natural
|
|
53
|
+
* end. It stays open until unsubscribed, so bound it with `takeUntil` if
|
|
54
|
+
* something else should end it.
|
|
55
|
+
*
|
|
56
|
+
* Errors do **not** terminate the stream. A dropped connection is expected, not
|
|
57
|
+
* exceptional: it is reported through `onError` and then retried, because
|
|
58
|
+
* surfacing it as an RxJS error would tear down every downstream operator and
|
|
59
|
+
* lose the subscriber's accumulated state on a transient blip.
|
|
60
|
+
*/
|
|
61
|
+
export function fromEventStream(options) {
|
|
62
|
+
const initialDelay = options.initialRetryDelayMs ?? DEFAULT_INITIAL_RETRY_MS;
|
|
63
|
+
const maxDelay = options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_MS;
|
|
64
|
+
const schedule = options.setTimeoutImpl ?? ((handler, ms) => setTimeout(handler, ms));
|
|
65
|
+
const cancel = options.clearTimeoutImpl ?? ((handle) => clearTimeout(handle));
|
|
66
|
+
const createSource = options.createSource ??
|
|
67
|
+
((url) => {
|
|
68
|
+
const Ctor = globalThis
|
|
69
|
+
.EventSource;
|
|
70
|
+
if (Ctor === undefined) {
|
|
71
|
+
throw new Error("No EventSource available. Pass `createSource` when running outside a browser.");
|
|
72
|
+
}
|
|
73
|
+
return new Ctor(url);
|
|
74
|
+
});
|
|
75
|
+
const url = buildStreamUrl(options.url, options.channels);
|
|
76
|
+
return new Observable((subscriber) => {
|
|
77
|
+
let source;
|
|
78
|
+
let retryHandle;
|
|
79
|
+
let delay = initialDelay;
|
|
80
|
+
let stopped = false;
|
|
81
|
+
const connect = () => {
|
|
82
|
+
if (stopped) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
source = createSource(url);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
// Cannot construct a source at all — a configuration fault, not a blip.
|
|
90
|
+
subscriber.error(error);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const type of options.types) {
|
|
94
|
+
source.addEventListener(type, (event) => {
|
|
95
|
+
const message = parseMessage(event.data);
|
|
96
|
+
if (message === undefined) {
|
|
97
|
+
options.onError?.(new Error(`Discarded a malformed "${type}" frame.`));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// A frame arrived, so the connection is healthy; reset the backoff.
|
|
101
|
+
delay = initialDelay;
|
|
102
|
+
subscriber.next(message);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
source.addEventListener("error", (event) => {
|
|
106
|
+
options.onError?.(event);
|
|
107
|
+
if (stopped) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
source?.close();
|
|
111
|
+
source = undefined;
|
|
112
|
+
retryHandle = schedule(connect, delay);
|
|
113
|
+
// Doubling, capped. Unbounded retries would hammer a recovering server.
|
|
114
|
+
delay = Math.min(delay * 2, maxDelay);
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
connect();
|
|
118
|
+
return () => {
|
|
119
|
+
stopped = true;
|
|
120
|
+
if (retryHandle !== undefined) {
|
|
121
|
+
cancel(retryHandle);
|
|
122
|
+
}
|
|
123
|
+
source?.close();
|
|
124
|
+
source = undefined;
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/streams/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Observable, type MonoTypeOperatorFunction, type OperatorFunction } from "rxjs";
|
|
2
|
+
import type { EventSchemaMap, EventTypeOf, SaasEvent, SaasEventOf } from "../events/registry.ts";
|
|
3
|
+
import type { EventsService } from "../events/service.ts";
|
|
4
|
+
/**
|
|
5
|
+
* An Observable of application events, from the in-process dispatcher.
|
|
6
|
+
*
|
|
7
|
+
* Streams, not promises: a sequence of events over time is exactly what an
|
|
8
|
+
* Observable is for. Nothing else in the core returns one — a one-shot database
|
|
9
|
+
* read stays a promise, because wrapping it would add a lifecycle to manage for
|
|
10
|
+
* no benefit.
|
|
11
|
+
*
|
|
12
|
+
* ## Lifecycle
|
|
13
|
+
*
|
|
14
|
+
* The events subscription is created when the Observable is subscribed and torn
|
|
15
|
+
* down when it is unsubscribed. Nothing is registered before the first
|
|
16
|
+
* subscriber, and nothing lingers after the last one — so a caller that forgets
|
|
17
|
+
* `takeUntil` leaks one handler, not a growing set.
|
|
18
|
+
*
|
|
19
|
+
* ## What this is not
|
|
20
|
+
*
|
|
21
|
+
* Not durable. A crash loses the reaction, not the event, because the event is
|
|
22
|
+
* already committed. Anything that must survive a restart belongs in
|
|
23
|
+
* `@digimuza/saas-core/jobs`, reading the persisted row — never in an operator's
|
|
24
|
+
* accumulated state.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fromEvents<S extends EventSchemaMap>(events: EventsService<S>): Observable<SaasEvent<S>>;
|
|
27
|
+
/**
|
|
28
|
+
* Narrow a stream to one or more event types, in the type system as well as at
|
|
29
|
+
* runtime.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* fromEvents(events).pipe(
|
|
33
|
+
* ofType("project.created", "project.deleted"),
|
|
34
|
+
* map((event) => event.data.projectId), // narrowed to those two payloads
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* A plain `filter((e) => e.type === "project.created")` would filter correctly
|
|
39
|
+
* but leave `data` as the union of every payload in the registry, so every
|
|
40
|
+
* downstream operator would need a cast.
|
|
41
|
+
*/
|
|
42
|
+
export declare function ofType<S extends EventSchemaMap, const K extends readonly EventTypeOf<S>[]>(...types: K): OperatorFunction<SaasEvent<S>, SaasEventOf<S, K[number]>>;
|
|
43
|
+
/**
|
|
44
|
+
* Keep only events belonging to one workspace.
|
|
45
|
+
*
|
|
46
|
+
* A server-side stream sees every workspace's events, because the in-process
|
|
47
|
+
* dispatcher is process-wide. This is the filter that makes a per-tenant stream
|
|
48
|
+
* out of it — and it is *not* an authorization boundary. The caller must already
|
|
49
|
+
* hold a verified `WorkspaceContext`; see `workspaces.requireContext`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function inWorkspace<T extends {
|
|
52
|
+
readonly workspaceId: string | null;
|
|
53
|
+
}>(workspaceId: string): MonoTypeOperatorFunction<T>;
|
|
54
|
+
/** Keep only events caused by one actor. */
|
|
55
|
+
export declare function byActor<T extends {
|
|
56
|
+
readonly actorId: string | null;
|
|
57
|
+
}>(actorId: string): MonoTypeOperatorFunction<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Project each event down to its payload.
|
|
60
|
+
*
|
|
61
|
+
* The type parameter is inferred from the *source*, not supplied by the caller —
|
|
62
|
+
* declaring it over the registry and the event key would leave both unbound at
|
|
63
|
+
* the call site and collapse `data` to `unknown`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function toData<E extends {
|
|
66
|
+
readonly data: unknown;
|
|
67
|
+
}>(): OperatorFunction<E, E["data"]>;
|
|
68
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/streams/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,wBAAwB,EAAE,KAAK,gBAAgB,EAAE,MAAM,MAAM,CAAC;AAGxF,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACjG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,cAAc,EACjD,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GACvB,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAW1B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,cAAc,EAAE,KAAK,CAAC,CAAC,SAAS,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,EACxF,GAAG,KAAK,EAAE,CAAC,GACV,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAM3D;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC3E,WAAW,EAAE,MAAM,GAClB,wBAAwB,CAAC,CAAC,CAAC,CAE7B;AAED,4CAA4C;AAC5C,wBAAgB,OAAO,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACnE,OAAO,EAAE,MAAM,GACd,wBAAwB,CAAC,CAAC,CAAC,CAE7B;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,KAAK,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAE7F"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
import { filter, map } from "rxjs/operators";
|
|
3
|
+
/**
|
|
4
|
+
* An Observable of application events, from the in-process dispatcher.
|
|
5
|
+
*
|
|
6
|
+
* Streams, not promises: a sequence of events over time is exactly what an
|
|
7
|
+
* Observable is for. Nothing else in the core returns one — a one-shot database
|
|
8
|
+
* read stays a promise, because wrapping it would add a lifecycle to manage for
|
|
9
|
+
* no benefit.
|
|
10
|
+
*
|
|
11
|
+
* ## Lifecycle
|
|
12
|
+
*
|
|
13
|
+
* The events subscription is created when the Observable is subscribed and torn
|
|
14
|
+
* down when it is unsubscribed. Nothing is registered before the first
|
|
15
|
+
* subscriber, and nothing lingers after the last one — so a caller that forgets
|
|
16
|
+
* `takeUntil` leaks one handler, not a growing set.
|
|
17
|
+
*
|
|
18
|
+
* ## What this is not
|
|
19
|
+
*
|
|
20
|
+
* Not durable. A crash loses the reaction, not the event, because the event is
|
|
21
|
+
* already committed. Anything that must survive a restart belongs in
|
|
22
|
+
* `@digimuza/saas-core/jobs`, reading the persisted row — never in an operator's
|
|
23
|
+
* accumulated state.
|
|
24
|
+
*/
|
|
25
|
+
export function fromEvents(events) {
|
|
26
|
+
return new Observable((subscriber) => {
|
|
27
|
+
/**
|
|
28
|
+
* `onAny` returns its own unsubscribe, which is returned as the teardown —
|
|
29
|
+
* so RxJS's own lifecycle drives the handler's, and there is one place where
|
|
30
|
+
* it is removed.
|
|
31
|
+
*/
|
|
32
|
+
return events.onAny((event) => {
|
|
33
|
+
subscriber.next(event);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Narrow a stream to one or more event types, in the type system as well as at
|
|
39
|
+
* runtime.
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* fromEvents(events).pipe(
|
|
43
|
+
* ofType("project.created", "project.deleted"),
|
|
44
|
+
* map((event) => event.data.projectId), // narrowed to those two payloads
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* A plain `filter((e) => e.type === "project.created")` would filter correctly
|
|
49
|
+
* but leave `data` as the union of every payload in the registry, so every
|
|
50
|
+
* downstream operator would need a cast.
|
|
51
|
+
*/
|
|
52
|
+
export function ofType(...types) {
|
|
53
|
+
const wanted = new Set(types);
|
|
54
|
+
return (source) => source.pipe(filter((event) => wanted.has(event.type)));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Keep only events belonging to one workspace.
|
|
58
|
+
*
|
|
59
|
+
* A server-side stream sees every workspace's events, because the in-process
|
|
60
|
+
* dispatcher is process-wide. This is the filter that makes a per-tenant stream
|
|
61
|
+
* out of it — and it is *not* an authorization boundary. The caller must already
|
|
62
|
+
* hold a verified `WorkspaceContext`; see `workspaces.requireContext`.
|
|
63
|
+
*/
|
|
64
|
+
export function inWorkspace(workspaceId) {
|
|
65
|
+
return filter((value) => value.workspaceId === workspaceId);
|
|
66
|
+
}
|
|
67
|
+
/** Keep only events caused by one actor. */
|
|
68
|
+
export function byActor(actorId) {
|
|
69
|
+
return filter((value) => value.actorId === actorId);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Project each event down to its payload.
|
|
73
|
+
*
|
|
74
|
+
* The type parameter is inferred from the *source*, not supplied by the caller —
|
|
75
|
+
* declaring it over the registry and the event key would leave both unbound at
|
|
76
|
+
* the call site and collapse `data` to `unknown`.
|
|
77
|
+
*/
|
|
78
|
+
export function toData() {
|
|
79
|
+
return map((event) => event.data);
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/streams/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAwD,MAAM,MAAM,CAAC;AACxF,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAK7C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,UAAU,CACxB,MAAwB;IAExB,OAAO,IAAI,UAAU,CAAe,CAAC,UAAU,EAAE,EAAE;QACjD;;;;WAIG;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC5B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,MAAM,CACpB,GAAG,KAAQ;IAEX,MAAM,MAAM,GAAG,IAAI,GAAG,CAAS,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,MAAM,EAAE,EAAE,CAChB,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,CAAC,KAAK,EAAsC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAC9E,CAAC;AACN,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,WAAmB;IAEnB,OAAO,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,KAAK,WAAW,CAAC,CAAC;AAC9D,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,OAAO,CACrB,OAAe;IAEf,OAAO,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM;IACpB,OAAO,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public streams surface: `@digimuza/saas-core/streams`. Server-only.
|
|
3
|
+
*
|
|
4
|
+
* RxJS is where *reactive behaviour* lives — filtering, combining, debouncing,
|
|
5
|
+
* aggregating, coordinating. It is deliberately none of the other things:
|
|
6
|
+
*
|
|
7
|
+
* - not the database (a one-shot read stays a promise);
|
|
8
|
+
* - not the durable job system (a crash loses a reaction, not an event);
|
|
9
|
+
* - not the realtime transport (that is `@digimuza/saas-core/realtime`).
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* event sources → application events → Observable → reactor → application/client
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* ## Server-side
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { fromEvents, ofType, inWorkspace } from "@digimuza/saas-core/streams";
|
|
19
|
+
* import { events } from "@digimuza/saas-core/events";
|
|
20
|
+
* import { debounceTime, takeUntil } from "rxjs/operators";
|
|
21
|
+
*
|
|
22
|
+
* const subscription = fromEvents(events)
|
|
23
|
+
* .pipe(
|
|
24
|
+
* inWorkspace(context.workspaceId),
|
|
25
|
+
* ofType("project.updated"),
|
|
26
|
+
* debounceTime(500),
|
|
27
|
+
* takeUntil(shutdown$), // every subscription needs a lifecycle
|
|
28
|
+
* )
|
|
29
|
+
* .subscribe(reindexProject);
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ## Browser
|
|
33
|
+
*
|
|
34
|
+
* `@digimuza/saas-core/streams/client` reads the SSE endpoint instead, and imports
|
|
35
|
+
* nothing from the server half of this package.
|
|
36
|
+
*
|
|
37
|
+
* ## The rule this module exists to respect
|
|
38
|
+
*
|
|
39
|
+
* Promises for one-time work, Observables for sequences over time. Nothing here
|
|
40
|
+
* converts a synchronous or single-shot API into a stream — `getDatabase()` is not
|
|
41
|
+
* an Observable, and `workspaces.get()` never will be.
|
|
42
|
+
*/
|
|
43
|
+
export { byActor, fromEvents, inWorkspace, ofType, toData, } from "./events.ts";
|
|
44
|
+
export { fromRealtime, type RealtimeStreamOptions } from "./realtime.ts";
|
|
45
|
+
export { latestByKey } from "./state.ts";
|
|
46
|
+
export { buildStreamUrl, fromEventStream, type ClientRealtimeMessage, type EventSourceLike, type EventStreamSourceOptions, type MessageEventLike, } from "./client.ts";
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/streams/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EACL,OAAO,EACP,UAAU,EACV,WAAW,EACX,MAAM,EACN,MAAM,GACP,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAAE,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEzE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public streams surface: `@digimuza/saas-core/streams`. Server-only.
|
|
3
|
+
*
|
|
4
|
+
* RxJS is where *reactive behaviour* lives — filtering, combining, debouncing,
|
|
5
|
+
* aggregating, coordinating. It is deliberately none of the other things:
|
|
6
|
+
*
|
|
7
|
+
* - not the database (a one-shot read stays a promise);
|
|
8
|
+
* - not the durable job system (a crash loses a reaction, not an event);
|
|
9
|
+
* - not the realtime transport (that is `@digimuza/saas-core/realtime`).
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* event sources → application events → Observable → reactor → application/client
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* ## Server-side
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { fromEvents, ofType, inWorkspace } from "@digimuza/saas-core/streams";
|
|
19
|
+
* import { events } from "@digimuza/saas-core/events";
|
|
20
|
+
* import { debounceTime, takeUntil } from "rxjs/operators";
|
|
21
|
+
*
|
|
22
|
+
* const subscription = fromEvents(events)
|
|
23
|
+
* .pipe(
|
|
24
|
+
* inWorkspace(context.workspaceId),
|
|
25
|
+
* ofType("project.updated"),
|
|
26
|
+
* debounceTime(500),
|
|
27
|
+
* takeUntil(shutdown$), // every subscription needs a lifecycle
|
|
28
|
+
* )
|
|
29
|
+
* .subscribe(reindexProject);
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ## Browser
|
|
33
|
+
*
|
|
34
|
+
* `@digimuza/saas-core/streams/client` reads the SSE endpoint instead, and imports
|
|
35
|
+
* nothing from the server half of this package.
|
|
36
|
+
*
|
|
37
|
+
* ## The rule this module exists to respect
|
|
38
|
+
*
|
|
39
|
+
* Promises for one-time work, Observables for sequences over time. Nothing here
|
|
40
|
+
* converts a synchronous or single-shot API into a stream — `getDatabase()` is not
|
|
41
|
+
* an Observable, and `workspaces.get()` never will be.
|
|
42
|
+
*/
|
|
43
|
+
export { byActor, fromEvents, inWorkspace, ofType, toData, } from "./events.js";
|
|
44
|
+
export { fromRealtime } from "./realtime.js";
|
|
45
|
+
export { latestByKey } from "./state.js";
|
|
46
|
+
export { buildStreamUrl, fromEventStream, } from "./client.js";
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/streams/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EACL,OAAO,EACP,UAAU,EACV,WAAW,EACX,MAAM,EACN,MAAM,GACP,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAA8B,MAAM,eAAe,CAAC;AAEzE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EACL,cAAc,EACd,eAAe,GAKhB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
import type { RealtimeService } from "../realtime/service.ts";
|
|
3
|
+
import type { RealtimeMessage } from "../realtime/transport.ts";
|
|
4
|
+
export interface RealtimeStreamOptions {
|
|
5
|
+
/** Resolved from the session. Never from a request body. */
|
|
6
|
+
readonly userId: string;
|
|
7
|
+
/** Requested channels. Unauthorized ones are dropped, not thrown on. */
|
|
8
|
+
readonly channels: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* An Observable of authorized realtime messages.
|
|
12
|
+
*
|
|
13
|
+
* The stream a *server* uses to react to delivery — a reactor coordinating
|
|
14
|
+
* behaviour across tenants, say. A browser should use
|
|
15
|
+
* `@digimuza/saas-core/streams/client` instead, which reads the SSE endpoint
|
|
16
|
+
* rather than subscribing to the transport directly.
|
|
17
|
+
*
|
|
18
|
+
* ## Authorization happens once, on subscribe
|
|
19
|
+
*
|
|
20
|
+
* `realtime.subscribe` verifies channel access before opening anything, so a
|
|
21
|
+
* subscriber never receives a channel it was not granted. Because that check is
|
|
22
|
+
* asynchronous and this is a synchronous Observable constructor, a subscriber
|
|
23
|
+
* that unsubscribes during the check is honoured: the subscription is closed
|
|
24
|
+
* immediately rather than left open until the next message.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fromRealtime(realtime: RealtimeService, options: RealtimeStreamOptions): Observable<RealtimeMessage>;
|
|
27
|
+
//# sourceMappingURL=realtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../src/streams/realtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAElC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IACpC,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,qBAAqB,GAC7B,UAAU,CAAC,eAAe,CAAC,CAkC7B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Observable } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* An Observable of authorized realtime messages.
|
|
4
|
+
*
|
|
5
|
+
* The stream a *server* uses to react to delivery — a reactor coordinating
|
|
6
|
+
* behaviour across tenants, say. A browser should use
|
|
7
|
+
* `@digimuza/saas-core/streams/client` instead, which reads the SSE endpoint
|
|
8
|
+
* rather than subscribing to the transport directly.
|
|
9
|
+
*
|
|
10
|
+
* ## Authorization happens once, on subscribe
|
|
11
|
+
*
|
|
12
|
+
* `realtime.subscribe` verifies channel access before opening anything, so a
|
|
13
|
+
* subscriber never receives a channel it was not granted. Because that check is
|
|
14
|
+
* asynchronous and this is a synchronous Observable constructor, a subscriber
|
|
15
|
+
* that unsubscribes during the check is honoured: the subscription is closed
|
|
16
|
+
* immediately rather than left open until the next message.
|
|
17
|
+
*/
|
|
18
|
+
export function fromRealtime(realtime, options) {
|
|
19
|
+
return new Observable((subscriber) => {
|
|
20
|
+
let closed = false;
|
|
21
|
+
let close;
|
|
22
|
+
void realtime
|
|
23
|
+
.subscribe({
|
|
24
|
+
userId: options.userId,
|
|
25
|
+
channels: options.channels,
|
|
26
|
+
onMessage: (message) => {
|
|
27
|
+
subscriber.next(message);
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
.then((subscription) => {
|
|
31
|
+
close = () => subscription.close();
|
|
32
|
+
/**
|
|
33
|
+
* Unsubscribed while authorization was in flight. Without this the
|
|
34
|
+
* transport subscription would stay open with nobody listening, holding a
|
|
35
|
+
* listener slot for the lifetime of the process.
|
|
36
|
+
*/
|
|
37
|
+
if (closed) {
|
|
38
|
+
void subscription.close();
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.catch((error) => {
|
|
42
|
+
subscriber.error(error);
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
closed = true;
|
|
46
|
+
void close?.();
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=realtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../src/streams/realtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAYlC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAyB,EACzB,OAA8B;IAE9B,OAAO,IAAI,UAAU,CAAkB,CAAC,UAAU,EAAE,EAAE;QACpD,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,IAAI,KAAwC,CAAC;QAE7C,KAAK,QAAQ;aACV,SAAS,CAAC;YACT,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;gBACrB,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;SACF,CAAC;aACD,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE;YACrB,KAAK,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;YAEnC;;;;eAIG;YACH,IAAI,MAAM,EAAE,CAAC;gBACX,KAAK,YAAY,CAAC,KAAK,EAAE,CAAC;YAC5B,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACxB,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEL,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,IAAI,CAAC;YACd,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Observable } from "rxjs";
|
|
2
|
+
/**
|
|
3
|
+
* Accumulate a stream into "the latest value per key".
|
|
4
|
+
*
|
|
5
|
+
* The one reactive-state shape worth wrapping, because the sharing is easy to get
|
|
6
|
+
* wrong: without `shareReplay`, every new subscriber re-runs the scan from an
|
|
7
|
+
* empty map and sees nothing until the next event arrives.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const progress$ = latestByKey(jobEvents$, (event) => event.data.runId);
|
|
11
|
+
* // Observable<ReadonlyMap<string, JobEvent>> — every run's most recent update.
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* `refCount: true` discards the accumulated map when the last subscriber leaves.
|
|
15
|
+
* Deliberate: a cache outliving its subscribers is a leak, and this is a *view* of
|
|
16
|
+
* durable data — the `events` table is where state lives if it must survive
|
|
17
|
+
* anything.
|
|
18
|
+
*
|
|
19
|
+
* Everything else is plain RxJS, on purpose. `debounceTime`, `throttleTime`,
|
|
20
|
+
* `bufferTime`, `scan`, `distinctUntilChanged` — including its
|
|
21
|
+
* `(comparator, keySelector)` form for comparing a projection — already exist, and
|
|
22
|
+
* re-exporting them under new names would add vocabulary without adding
|
|
23
|
+
* behaviour.
|
|
24
|
+
*/
|
|
25
|
+
export declare function latestByKey<T>(source: Observable<T>, keyOf: (value: T) => string): Observable<ReadonlyMap<string, T>>;
|
|
26
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/streams/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAGvC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EACrB,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,GAC1B,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAWpC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { scan, shareReplay } from "rxjs/operators";
|
|
2
|
+
/**
|
|
3
|
+
* Accumulate a stream into "the latest value per key".
|
|
4
|
+
*
|
|
5
|
+
* The one reactive-state shape worth wrapping, because the sharing is easy to get
|
|
6
|
+
* wrong: without `shareReplay`, every new subscriber re-runs the scan from an
|
|
7
|
+
* empty map and sees nothing until the next event arrives.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const progress$ = latestByKey(jobEvents$, (event) => event.data.runId);
|
|
11
|
+
* // Observable<ReadonlyMap<string, JobEvent>> — every run's most recent update.
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* `refCount: true` discards the accumulated map when the last subscriber leaves.
|
|
15
|
+
* Deliberate: a cache outliving its subscribers is a leak, and this is a *view* of
|
|
16
|
+
* durable data — the `events` table is where state lives if it must survive
|
|
17
|
+
* anything.
|
|
18
|
+
*
|
|
19
|
+
* Everything else is plain RxJS, on purpose. `debounceTime`, `throttleTime`,
|
|
20
|
+
* `bufferTime`, `scan`, `distinctUntilChanged` — including its
|
|
21
|
+
* `(comparator, keySelector)` form for comparing a projection — already exist, and
|
|
22
|
+
* re-exporting them under new names would add vocabulary without adding
|
|
23
|
+
* behaviour.
|
|
24
|
+
*/
|
|
25
|
+
export function latestByKey(source, keyOf) {
|
|
26
|
+
return source.pipe(scan((state, value) => {
|
|
27
|
+
// A fresh Map per emission, so a subscriber holding an earlier value is not
|
|
28
|
+
// mutated underneath it — the usual expectation for a stream of states.
|
|
29
|
+
const next = new Map(state);
|
|
30
|
+
next.set(keyOf(value), value);
|
|
31
|
+
return next;
|
|
32
|
+
}, new Map()), shareReplay({ bufferSize: 1, refCount: true }));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/streams/state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,WAAW,CACzB,MAAqB,EACrB,KAA2B;IAE3B,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACpB,4EAA4E;QAC5E,wEAAwE;QACxE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,IAAI,GAAG,EAAa,CAAC,EACxB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAC/C,CAAC;AACJ,CAAC"}
|