@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
package/docs/logger.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Logger
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
import { logger, createLogger, createConfiguredLogger, setDefaultLogger } from "@digimuza/saas-core/logger";
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
> Orientation and recipes: [`src/logger/README.md`](../src/logger/README.md).
|
|
8
|
+
|
|
9
|
+
Structured logging with bound context and redaction. No logging dependency — a
|
|
10
|
+
level check, a field merge and a JSON line do not justify one, and the sink seam
|
|
11
|
+
makes adding pino or a hosted collector an adapter rather than a migration.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createConfiguredLogger, setDefaultLogger } from "@digimuza/saas-core/logger";
|
|
19
|
+
|
|
20
|
+
setDefaultLogger(createConfiguredLogger());
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`createConfiguredLogger()` reads the `app` config section: `LOG_LEVEL` for the
|
|
24
|
+
level, `APP_ENV` to choose JSON (production) or human-readable (everything else),
|
|
25
|
+
and binds `app` and `env` so several services shipping to one aggregator stay
|
|
26
|
+
distinguishable.
|
|
27
|
+
|
|
28
|
+
Until you call it, the core logs at `info` to stdout as JSON. Deliberate:
|
|
29
|
+
importing a core module must never require configuration, or a migration script
|
|
30
|
+
would need `LOG_LEVEL` set to run. That is also why `src/logger/default.ts`
|
|
31
|
+
imports nothing but the logger itself — if it pulled in `config`, every module
|
|
32
|
+
that logs would transitively depend on configuration.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## `Logger`
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
interface Logger {
|
|
40
|
+
readonly level: LogLevel; // "debug" | "info" | "warn" | "error"
|
|
41
|
+
|
|
42
|
+
debug(message: string, fields?: LogFields): void;
|
|
43
|
+
info(message: string, fields?: LogFields): void;
|
|
44
|
+
warn(message: string, fields?: LogFields): void;
|
|
45
|
+
error(message: string, fields?: LogFields & { error?: unknown }): void;
|
|
46
|
+
|
|
47
|
+
child(fields: LogFields): Logger;
|
|
48
|
+
isEnabled(level: LogLevel): boolean;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### `child(fields)`
|
|
53
|
+
|
|
54
|
+
The reason the module exists:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const log = logger.child({ requestId, workspaceId: context.workspaceId, userId: user.id });
|
|
58
|
+
|
|
59
|
+
log.info("project created", { projectId });
|
|
60
|
+
log.warn("slow query", { durationMs });
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Bind at the edge and every line beneath is attributable, without threading
|
|
64
|
+
identifiers through call signatures. Children nest, inherit the level, share the
|
|
65
|
+
sink, and never affect their parent.
|
|
66
|
+
|
|
67
|
+
Per-call fields override bound ones.
|
|
68
|
+
|
|
69
|
+
### Level checks happen first
|
|
70
|
+
|
|
71
|
+
A suppressed record is never merged or serialised, and its field object is never
|
|
72
|
+
even evaluated:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
log.debug("plan", { get expensive() { return explain(); } }); // getter not called
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
So debug logging is not something you strip out for performance. `isEnabled` is
|
|
79
|
+
there for work you would not want to do regardless — an extra query, a large
|
|
80
|
+
serialisation.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Errors
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
log.error("indexing failed", { projectId, error });
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`error` accepts any throwable and is serialised to `{ name, message, stack, code?,
|
|
91
|
+
cause?, errors? }`:
|
|
92
|
+
|
|
93
|
+
- **`stack`** is capped at 20 lines.
|
|
94
|
+
- **`cause`** is followed up to 4 levels. Dropping it would leave "query failed"
|
|
95
|
+
with no indication that the connection was refused.
|
|
96
|
+
- **`AggregateError.errors`** is kept, or "Invalid configuration in 3 sections"
|
|
97
|
+
would say nothing about which.
|
|
98
|
+
- **`code`** is kept when present, so [domain errors](./errors.md) stay filterable.
|
|
99
|
+
- **The message is value-scanned**, because it is the likeliest place for a token
|
|
100
|
+
to end up by accident.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Redaction
|
|
105
|
+
|
|
106
|
+
On by default. The core handles API keys, session cookies, invitation tokens and
|
|
107
|
+
webhook secrets; a logger that faithfully serialises whatever it is handed is the
|
|
108
|
+
easiest way to move all of them into an aggregator a wider audience can read.
|
|
109
|
+
|
|
110
|
+
Two independent passes, because either alone has a hole.
|
|
111
|
+
|
|
112
|
+
### By key
|
|
113
|
+
|
|
114
|
+
Case-insensitive **substring** match, so `workosCookiePassword` and
|
|
115
|
+
`cookie_password` are both caught without enumerating spellings.
|
|
116
|
+
|
|
117
|
+
`DEFAULT_REDACT_KEYS`: `password`, `passwd`, `secret`, `token`, `apikey`,
|
|
118
|
+
`api_key`, `authorization`, `cookie`, `credential`, `privatekey`, `private_key`,
|
|
119
|
+
`signature`, `keyhash`, `key_hash`, `sessiondata`, `bearer`, `otp`, `mfa`.
|
|
120
|
+
|
|
121
|
+
### By value
|
|
122
|
+
|
|
123
|
+
Catches a credential under an innocent key — `{ input: "dgk_…" }`:
|
|
124
|
+
|
|
125
|
+
| Pattern | Source |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `<prefix>_<32 hex>_<64 hex>` | this package's [API keys](./api-keys.md) |
|
|
128
|
+
| `whsec_…` | this package's [webhook secrets](./webhooks.md) |
|
|
129
|
+
| `wsk_…` | Revolut webhook secrets |
|
|
130
|
+
| `sk_`/`rk_`/`pk_` + `live`/`test` | Stripe-shaped provider keys |
|
|
131
|
+
| `re_…` | Resend keys |
|
|
132
|
+
| `eyJ….….…` | JWTs, including the sealed session cookie |
|
|
133
|
+
| `Bearer …` | an authorization header value |
|
|
134
|
+
|
|
135
|
+
Anchored on **prefixes this system and its providers issue**, not on entropy.
|
|
136
|
+
"Looks random" would strip ids, hashes and slugs, and a log with every identifier
|
|
137
|
+
removed is not useful. There is a test asserting UUIDs, slugs, digests and realtime
|
|
138
|
+
channel names all survive.
|
|
139
|
+
|
|
140
|
+
### Configuring it
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
createLogger({
|
|
144
|
+
redact: {
|
|
145
|
+
additionalKeys: ["ssn", "dateOfBirth"], // added to the defaults
|
|
146
|
+
// keys: [...], // replaces them entirely
|
|
147
|
+
// disableValueScanning: true, // key redaction stays on
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Over-redaction is the intended failure mode.** `tokenCount` is redacted because
|
|
153
|
+
the key contains `token`. Replace `keys` if that matters more than the default
|
|
154
|
+
safety.
|
|
155
|
+
|
|
156
|
+
### Bounds
|
|
157
|
+
|
|
158
|
+
A pathological object cannot stall the process or fill a disk: depth 6, 100 array
|
|
159
|
+
items, 8192-character strings, cycles replaced with `[circular]`, functions never
|
|
160
|
+
serialised (their source can embed a literal secret).
|
|
161
|
+
|
|
162
|
+
### Redacting outside the logger
|
|
163
|
+
|
|
164
|
+
`createRedactor(options)` returns the same function the logger uses, for anywhere
|
|
165
|
+
else a value crosses a boundary you do not fully trust — an error-tracker
|
|
166
|
+
integration, a support-tool response, a debug endpoint:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { createRedactor } from "@digimuza/saas-core/logger";
|
|
170
|
+
|
|
171
|
+
const redact = createRedactor({ additionalKeys: ["ssn"] });
|
|
172
|
+
Sentry.setContext("request", redact(payload) as Record<string, unknown>);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
It returns a plain, JSON-safe structure with the same bounds and both passes
|
|
176
|
+
applied.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Sinks
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
interface LogSink {
|
|
184
|
+
readonly name: string;
|
|
185
|
+
write(record: LogRecord): void;
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
One method, because that is the whole job. Sampling, buffering and shipping belong
|
|
190
|
+
to whatever you plug in.
|
|
191
|
+
|
|
192
|
+
| Sink | Use |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| `createJsonSink({ write? })` | Production. One JSON object per line. |
|
|
195
|
+
| `createPrettySink({ write? })` | Development. Lossy by design. |
|
|
196
|
+
| `createMemorySink()` | Tests. `records`, `at(level)`, `clear()`. |
|
|
197
|
+
| `createNoopSink()` | Silence. |
|
|
198
|
+
| `createMultiSink([...])` | stdout plus an aggregator. |
|
|
199
|
+
|
|
200
|
+
**Everything goes to stdout**, including errors. Many collectors treat anything on
|
|
201
|
+
stderr as an error regardless of the record's own level, which would double-count
|
|
202
|
+
warnings and mislabel info. Pass `write` to change that.
|
|
203
|
+
|
|
204
|
+
**Fields cannot shadow `level`, `time` or `message`** — they are spread under the
|
|
205
|
+
reserved keys, so a record cannot lie about itself.
|
|
206
|
+
|
|
207
|
+
### Plugging in pino
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import pino from "pino";
|
|
211
|
+
|
|
212
|
+
const instance = pino();
|
|
213
|
+
const sink: LogSink = {
|
|
214
|
+
name: "pino",
|
|
215
|
+
write: (record) => instance[record.level]({ ...record.fields, time: record.time }, record.message),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
setDefaultLogger(createLogger({ level: "info", sink }));
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Logging never fails the caller
|
|
224
|
+
|
|
225
|
+
A throwing sink, an unserialisable field, a cyclic object — all swallowed.
|
|
226
|
+
Logging is diagnostic; it is never a reason for the logged operation to fail.
|
|
227
|
+
`createMultiSink` also isolates one broken sink from the rest.
|
|
228
|
+
|
|
229
|
+
There is deliberately no fallback reporting when a sink throws: there is nowhere
|
|
230
|
+
left to report it.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## What the core logs
|
|
235
|
+
|
|
236
|
+
Very little, on purpose — a library that logs chattily is noise in someone else's
|
|
237
|
+
application. Currently only failures it would otherwise swallow:
|
|
238
|
+
|
|
239
|
+
| Where | Record |
|
|
240
|
+
| --- | --- |
|
|
241
|
+
| [events](./events.md) | `event handler failed` — a subscriber threw |
|
|
242
|
+
| [realtime](./realtime.md) | `realtime transport error`, `realtime delivery error` |
|
|
243
|
+
|
|
244
|
+
Both are cases where the operation deliberately succeeds and the failure would
|
|
245
|
+
otherwise be invisible. Pass `onHandlerError` / `onError` to route them yourself.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Testing
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
const sink = createMemorySink();
|
|
253
|
+
const log = createLogger({ sink, level: "debug", now: () => fixedClock });
|
|
254
|
+
|
|
255
|
+
log.error("failed", { error });
|
|
256
|
+
|
|
257
|
+
expect(sink.at("error")[0]?.fields.error).toMatchObject({ name: "Error" });
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
`silenceDefaultLogger()` quiets the core in a test run, and
|
|
261
|
+
`getDefaultLogger()` returns whatever is currently installed — useful for asserting
|
|
262
|
+
that setup ran, or for a module that wants the core's logger without importing
|
|
263
|
+
`config` through the `/logger` entry point.
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
import { getDefaultLogger, setDefaultLogger, silenceDefaultLogger } from "@digimuza/saas-core/logger";
|
|
267
|
+
|
|
268
|
+
beforeEach(() => silenceDefaultLogger());
|
|
269
|
+
```
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Notifications
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
import { notifications, createNotificationsService } from "@digimuza/saas-core/notifications";
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
> Orientation and recipes: [`src/notifications/README.md`](../src/notifications/README.md).
|
|
8
|
+
|
|
9
|
+
In-app notifications addressed to a person.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## The ownership model
|
|
14
|
+
|
|
15
|
+
A notification **belongs to its recipient**. `workspace_id` only records which
|
|
16
|
+
tenant it concerns.
|
|
17
|
+
|
|
18
|
+
Every read filters on `user_id`, so one user's notifications can never surface in
|
|
19
|
+
another's list regardless of the workspace filter. That is why `list`, `markRead`
|
|
20
|
+
and `remove` all take a `userId` rather than a `WorkspaceContext`.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## `notifications.create(input, tx?)`
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
function create(input: {
|
|
28
|
+
userId: string; // the recipient
|
|
29
|
+
workspaceId?: string | null; // null for account-level notices
|
|
30
|
+
type: string;
|
|
31
|
+
title: string; // max 200 characters
|
|
32
|
+
body?: string;
|
|
33
|
+
url?: string; // relative, so it survives a domain change
|
|
34
|
+
data?: Record<string, unknown>;
|
|
35
|
+
dedupeKey?: string;
|
|
36
|
+
}, tx?: CoreDatabase): Promise<Notification>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
await notifications.create({
|
|
41
|
+
userId: member.id,
|
|
42
|
+
workspaceId: context.workspaceId,
|
|
43
|
+
type: "project.shared",
|
|
44
|
+
title: "Apollo was shared with you",
|
|
45
|
+
url: `/w/${slug}/projects/${projectId}`,
|
|
46
|
+
dedupeKey: `project.shared:${projectId}:${member.id}`,
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Notifying a non-member throws `NotFoundError`.** The title alone would tell them
|
|
51
|
+
the workspace exists, and something about what happens in it. Verified whenever a
|
|
52
|
+
`workspaces` service is wired — the default export wires it.
|
|
53
|
+
|
|
54
|
+
## `notifications.createMany(inputs)`
|
|
55
|
+
|
|
56
|
+
Skips any that duplicate an existing dedupe key, and returns only what was created.
|
|
57
|
+
|
|
58
|
+
## `notifications.list(userId, options?)`
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
function list(userId: string, options?: {
|
|
62
|
+
unreadOnly?: boolean;
|
|
63
|
+
workspaceId?: string | null; // `null` selects account-level notices
|
|
64
|
+
types?: readonly string[];
|
|
65
|
+
limit?: number; // default 50, capped at 200
|
|
66
|
+
before?: Date; // keyset pagination
|
|
67
|
+
}): Promise<Notification[]>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`userId` must come from the session.
|
|
71
|
+
|
|
72
|
+
Note the three-way `workspaceId`: omitted means every workspace, a value means one
|
|
73
|
+
workspace, and explicit `null` means only account-level notices.
|
|
74
|
+
|
|
75
|
+
## `notifications.countUnread(userId, options?)`
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
function countUnread(userId: string, options?: { workspaceId?: string | null }): Promise<number>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The bell-icon badge. Backed by an index on `(user_id, read_at, created_at desc)`,
|
|
82
|
+
so unread rows are found without scanning a long history.
|
|
83
|
+
|
|
84
|
+
## `notifications.markRead(userId, ids)` / `markAllRead(userId, options?)`
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
function markRead(userId: string, notificationIds: readonly string[]): Promise<number>
|
|
88
|
+
function markAllRead(userId: string, options?: { workspaceId?: string | null }): Promise<number>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Both return how many rows actually changed, so marking an already-read notification
|
|
92
|
+
returns `0` rather than erroring.
|
|
93
|
+
|
|
94
|
+
Caller-scoped: another user's id matches nothing rather than raising, because
|
|
95
|
+
raising would confirm it exists.
|
|
96
|
+
|
|
97
|
+
## `notifications.remove(userId, id)`
|
|
98
|
+
|
|
99
|
+
Throws `NotFoundError` for an id the caller does not own — same reasoning.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Deduplication
|
|
104
|
+
|
|
105
|
+
`(user_id, type, dedupe_key)` is a **partial** unique index. `create` returns the
|
|
106
|
+
existing row for a reused key, so the call is safe to retry.
|
|
107
|
+
|
|
108
|
+
Scoped per recipient *and* per type, deliberately: one underlying fact legitimately
|
|
109
|
+
notifies several people, and two event types may derive a key from the same
|
|
110
|
+
request.
|
|
111
|
+
|
|
112
|
+
> Because the index is partial (`where dedupe_key is not null`), the `ON CONFLICT`
|
|
113
|
+
> clause carries a matching predicate. PostgreSQL refuses to infer a partial index
|
|
114
|
+
> as the arbiter otherwise, and every insert fails with `42P10`.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Consuming events
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
events.on("workspace.member.created", async (event) => {
|
|
122
|
+
await notifications.create({
|
|
123
|
+
userId: event.data.userId,
|
|
124
|
+
workspaceId: event.workspaceId,
|
|
125
|
+
type: "workspace.joined",
|
|
126
|
+
title: "Welcome to the workspace",
|
|
127
|
+
dedupeKey: event.id, // handlers can run twice
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**A handler is not durable.** A crash between the commit and the handler loses the
|
|
133
|
+
notification. For anything that must not be missed, create it in the same
|
|
134
|
+
transaction as the change (`create(input, tx)`), or from a
|
|
135
|
+
[Trigger.dev task](./jobs.md) reading the persisted event.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Email notifications are not automatic
|
|
140
|
+
|
|
141
|
+
Which notifications also warrant an email is a product decision, and per-user
|
|
142
|
+
preferences, digests and quiet hours are product features rather than core ones.
|
|
143
|
+
Compose the two explicitly:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
const notification = await notifications.create({ /* ... */ });
|
|
147
|
+
|
|
148
|
+
if (await wantsEmail(member.id, notification.type)) {
|
|
149
|
+
await email.sendTemplate({
|
|
150
|
+
to: profile.email,
|
|
151
|
+
template: "project.shared",
|
|
152
|
+
data: { projectName },
|
|
153
|
+
idempotencyKey: notification.id, // sending is not transactional
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Lifecycle
|
|
161
|
+
|
|
162
|
+
Notifications **cascade away** when the recipient or the workspace is deleted —
|
|
163
|
+
unlike [audit entries](./audit.md), which must outlive their subject. An inbox is
|
|
164
|
+
disposable; a trail is not.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Custom construction
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
createNotificationsService({
|
|
172
|
+
db,
|
|
173
|
+
workspaces, // omit to skip the membership check
|
|
174
|
+
now: () => fixedClock,
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Omitting `workspaces` disables membership verification, which is only safe when
|
|
179
|
+
every caller already holds a verified context.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Publishing
|
|
2
|
+
|
|
3
|
+
`@digimuza/saas-core` publishes to **npmjs.com with restricted access** — only your
|
|
4
|
+
npm organisation can install it, matching `license: UNLICENSED`.
|
|
5
|
+
|
|
6
|
+
The workflow is [`.github/workflows/publish.yml`](../.github/workflows/publish.yml).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## One-time setup
|
|
11
|
+
|
|
12
|
+
1. **Create an npm automation token.** npmjs.com → Access Tokens → Generate →
|
|
13
|
+
*Automation*. Granular tokens work too; the token needs publish rights on the
|
|
14
|
+
`@digimuza` scope.
|
|
15
|
+
|
|
16
|
+
2. **Add it as a repository secret** named `NPM_TOKEN`:
|
|
17
|
+
Settings → Secrets and variables → Actions → New repository secret.
|
|
18
|
+
|
|
19
|
+
3. **Confirm the org can host restricted packages.** Restricted scoped packages
|
|
20
|
+
need a paid npm organisation. Publishing fails with a payment error otherwise —
|
|
21
|
+
at which point the options are a paid plan, going public, or GitHub Packages.
|
|
22
|
+
|
|
23
|
+
4. **The `repository` field** points at `github.com/digimuza/sass-core` — note the
|
|
24
|
+
single `a`. The npm package is `@digimuza/saas-core`; only the repository is
|
|
25
|
+
spelled `sass-core`. npm links a package to its source from this field, so the
|
|
26
|
+
mismatch is intentional rather than a typo to "fix".
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Releasing
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# 1. Bump the version. This is the only thing that decides what gets published.
|
|
34
|
+
npm version minor # or patch / major
|
|
35
|
+
|
|
36
|
+
# 2. Push the commit and the tag it created.
|
|
37
|
+
git push --follow-tags
|
|
38
|
+
|
|
39
|
+
# 3. Create a GitHub Release for that tag.
|
|
40
|
+
# Publishing the release triggers the workflow.
|
|
41
|
+
gh release create v0.15.0 --generate-notes
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The tag must match `package.json` — the workflow checks, because tagging `v0.15.0`
|
|
45
|
+
while the manifest still says `0.14.0` publishes the wrong version under the right
|
|
46
|
+
tag, and the mistake is invisible afterwards.
|
|
47
|
+
|
|
48
|
+
### Rehearsing it
|
|
49
|
+
|
|
50
|
+
`workflow_dispatch` runs every check and packs the tarball without publishing.
|
|
51
|
+
`dry-run` defaults to **true**, so an accidental manual run cannot publish:
|
|
52
|
+
|
|
53
|
+
Actions → Publish → Run workflow → leave *dry-run* ticked.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## What the workflow checks before publishing
|
|
58
|
+
|
|
59
|
+
Each of these has failed for real at some point, which is why it is a step rather
|
|
60
|
+
than a comment.
|
|
61
|
+
|
|
62
|
+
| Gate | Guards against |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `npm ci` | a lockfile that disagrees with `package.json` |
|
|
65
|
+
| Typecheck | — |
|
|
66
|
+
| Test suite | — |
|
|
67
|
+
| Clean build | a `dist` that only works incrementally |
|
|
68
|
+
| **Migration drift** | shipping types that describe tables no migration creates |
|
|
69
|
+
| **Version not already published** | a confusing mid-publish failure |
|
|
70
|
+
| **Tag matches manifest** | publishing 0.14.0 as v0.15.0 |
|
|
71
|
+
| **Tarball contents** | a missing `drizzle/`, or shipping `src/` and `tests/` |
|
|
72
|
+
| **Loads under raw Node ESM** | a broken `.js` specifier in the built output |
|
|
73
|
+
|
|
74
|
+
The tarball check asserts both directions. A missing `drizzle/meta/_journal.json`
|
|
75
|
+
would leave consumers unable to migrate and would look fine until someone tried;
|
|
76
|
+
a stray `src/` would double the package size and ship the tests.
|
|
77
|
+
|
|
78
|
+
The raw-Node check matters because the package ships real ESM with `.js`
|
|
79
|
+
specifiers and is explicitly meant to load without a bundler — see
|
|
80
|
+
[db.md](./db.md) and `src/README.md`. A bad specifier would otherwise surface in
|
|
81
|
+
someone's migration script.
|
|
82
|
+
|
|
83
|
+
### Scripts are skipped at the publish step
|
|
84
|
+
|
|
85
|
+
`prepublishOnly` runs `clean && build && test`. The gates already did all three, so
|
|
86
|
+
the publish itself uses `--ignore-scripts` to stay a single fast auditable step
|
|
87
|
+
rather than repeating a two-minute suite.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Notes
|
|
92
|
+
|
|
93
|
+
**Concurrency is serialised, not cancelled.** Two publishes racing would both try
|
|
94
|
+
to claim the same version and the loser fails halfway through; cancelling one
|
|
95
|
+
mid-flight is worse than queueing it.
|
|
96
|
+
|
|
97
|
+
**No provenance attestation.** npm provenance requires a public package and a
|
|
98
|
+
public source repository. Going public later means adding `--provenance` and
|
|
99
|
+
`id-token: write`, and replacing `UNLICENSED` with a real licence.
|
|
100
|
+
|
|
101
|
+
**Publishing is never automatic on merge.** A release is a decision, so it takes
|
|
102
|
+
either a GitHub Release or an explicit manual run.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Switching to GitHub Packages
|
|
107
|
+
|
|
108
|
+
If a paid npm organisation is not worth it, GitHub Packages hosts private packages
|
|
109
|
+
free on any plan and authenticates with the built-in `GITHUB_TOKEN` — no secret to
|
|
110
|
+
manage:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
permissions:
|
|
114
|
+
contents: read
|
|
115
|
+
packages: write
|
|
116
|
+
|
|
117
|
+
- uses: actions/setup-node@v4
|
|
118
|
+
with:
|
|
119
|
+
node-version: 22
|
|
120
|
+
registry-url: https://npm.pkg.github.com
|
|
121
|
+
|
|
122
|
+
- run: npm publish --ignore-scripts
|
|
123
|
+
env:
|
|
124
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Consumers then need `@digimuza:registry=https://npm.pkg.github.com` in their
|
|
128
|
+
`.npmrc`, which is the real cost of the switch.
|