@cosmicdrift/kumiko-framework 0.367.0 → 0.369.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -7
- package/README.md +1 -1
- package/agents/examples/access-control.md +2 -2
- package/agents/examples/anonymous-access-multitenant.md +2 -2
- package/agents/examples/anonymous-access.md +3 -3
- package/agents/examples/apex-landing.md +1 -1
- package/agents/examples/apex-surface-auth.md +6 -7
- package/agents/examples/api-worker-split.md +7 -8
- package/agents/examples/auth-claims.md +2 -1
- package/agents/examples/auth-foundation-providers.md +3 -3
- package/agents/examples/auth-mfa.md +3 -4
- package/agents/examples/basic-entity.md +3 -4
- package/agents/examples/chat-channels.md +4 -4
- package/agents/examples/compliance-profiles-demo.md +2 -3
- package/agents/examples/cross-feature-events.md +4 -4
- package/agents/examples/crypto-shredding-hr.md +14 -9
- package/agents/examples/currencies-global.md +3 -4
- package/agents/examples/currencies-per-tenant.md +3 -4
- package/agents/examples/custom-fields-basic.md +2 -3
- package/agents/examples/custom-handlers.md +3 -5
- package/agents/examples/dashboard-stats.md +2 -3
- package/agents/examples/delivery-notifications.md +5 -6
- package/agents/examples/e2e-generator.md +1 -1
- package/agents/examples/embedded-entity-form.md +2 -3
- package/agents/examples/embedded.md +2 -3
- package/agents/examples/encrypted-tenant-config.md +5 -6
- package/agents/examples/error-contract.md +3 -4
- package/agents/examples/event-sourcing.md +19 -23
- package/agents/examples/feature-toggles.md +4 -4
- package/agents/examples/field-access.md +3 -4
- package/agents/examples/files-post-processing.md +12 -13
- package/agents/examples/golden-path.md +208 -0
- package/agents/examples/idempotency.md +2 -2
- package/agents/examples/inbound-mail-triage.md +7 -7
- package/agents/examples/lane-routing.md +3 -3
- package/agents/examples/ledger-banking.md +2 -3
- package/agents/examples/ledger-invoicing.md +2 -3
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/lifecycle-hooks.md +3 -3
- package/agents/examples/managed-config.md +3 -4
- package/agents/examples/managed-pages.md +2 -2
- package/agents/examples/notes-history-basic.md +4 -5
- package/agents/examples/ownership.md +3 -5
- package/agents/examples/pdf-audit-export.md +2 -3
- package/agents/examples/pipeline-basics.md +2 -2
- package/agents/examples/public-share-token.md +3 -3
- package/agents/examples/rate-limiting.md +2 -1
- package/agents/examples/realtime-sse.md +2 -3
- package/agents/examples/record-detail-layout.md +1 -1
- package/agents/examples/reference-data.md +3 -3
- package/agents/examples/relations.md +2 -2
- package/agents/examples/screens-nav.md +1 -1
- package/agents/examples/search.md +2 -2
- package/agents/examples/secrets-demo.md +8 -8
- package/agents/examples/security-baseline.md +2 -2
- package/agents/examples/session-revocation.md +7 -8
- package/agents/examples/state-machine.md +181 -165
- package/agents/examples/tags-basic.md +2 -3
- package/agents/examples/tenant-isolation.md +2 -3
- package/agents/examples/tenant-settings.md +2 -2
- package/agents/examples/tier-admin.md +3 -4
- package/agents/examples/timezones.md +3 -3
- package/agents/examples/user-data-rights.md +12 -8
- package/agents/examples/user-profile.md +3 -3
- package/agents/examples/webhook-step.md +2 -3
- package/agents/examples/wizard-form.md +4 -5
- package/agents/examples/workflow-engine.md +7 -3
- package/agents/examples/writeform-section.md +1 -1
- package/agents/examples.md +1 -0
- package/agents/features.md +4 -4
- package/dist/consumer-cli.js +2 -3
- package/dist/crypto/index.d.ts +1 -1
- package/dist/crypto/index.js +1 -1
- package/dist/crypto/owner-transition-rekey.js +2 -0
- package/dist/crypto/pg-kms-adapter.js +1 -1
- package/dist/crypto/subject-resolver.d.ts +2 -0
- package/dist/crypto/subject-resolver.js +30 -15
- package/dist/db/bun-db-deprecated.d.ts +10 -0
- package/dist/{bun-db/index.js → db/bun-db-deprecated.js} +4 -3
- package/dist/db/config-seed.js +1 -1
- package/dist/db/connection.d.ts +23 -5
- package/dist/db/connection.js +10 -5
- package/dist/db/dialect.js +1 -1
- package/dist/db/entity-field-encryption.d.ts +5 -0
- package/dist/db/entity-field-encryption.js +29 -0
- package/dist/db/entity-table-meta.js +6 -5
- package/dist/db/event-store-executor-read.js +26 -5
- package/dist/db/index.d.ts +4 -4
- package/dist/db/index.js +1 -1
- package/dist/db/migrate-runner.js +1 -1
- package/dist/db/queries/backfill-pii.js +16 -9
- package/dist/db/queries/ddl.js +1 -1
- package/dist/db/queries/event-store.d.ts +2 -0
- package/dist/db/queries/event-store.js +26 -0
- package/dist/db/queries/test-stack.js +1 -1
- package/dist/db/query.d.ts +105 -1
- package/dist/db/query.js +1450 -3
- package/dist/{stack/table-helpers.d.ts → db/table-push.d.ts} +5 -15
- package/dist/{stack/table-helpers.js → db/table-push.js} +6 -46
- package/dist/db/tenant-db.d.ts +11 -0
- package/dist/db/tenant-db.js +51 -12
- package/dist/derivatives/derivatives-context.d.ts +1 -1
- package/dist/derivatives/derivatives-context.js +1 -1
- package/dist/engine/boot-validator/gdpr-storage.js +3 -3
- package/dist/engine/boot-validator/pii-retention.js +3 -3
- package/dist/engine/boot-validator/record-owned.js +3 -3
- package/dist/engine/boot-validator/screens.js +37 -1
- package/dist/engine/bridge-stub.d.ts +4 -0
- package/dist/engine/bridge-stub.js +62 -0
- package/dist/engine/build-config-feature-schema.js +197 -65
- package/dist/engine/event-helpers.d.ts +2 -1
- package/dist/engine/event-helpers.js +1 -0
- package/dist/engine/extension-selector-plugins.d.ts +3 -0
- package/dist/engine/extension-selector-plugins.js +14 -0
- package/dist/engine/feature-config-events-jobs.d.ts +2 -0
- package/dist/engine/feature-config-events-jobs.js +3 -2
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +1 -0
- package/dist/es-ops/operations-schema.js +1 -1
- package/dist/event-store/admin-api.js +1 -1
- package/dist/event-store/archive.js +1 -1
- package/dist/event-store/errors.d.ts +2 -1
- package/dist/event-store/errors.js +6 -2
- package/dist/event-store/event-store.js +17 -9
- package/dist/event-store/events-schema.js +1 -1
- package/dist/event-store/rebuild-dead-letter.js +3 -3
- package/dist/event-store/snapshot.js +1 -1
- package/dist/event-store/upcaster-dead-letter.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +1 -1
- package/dist/files/file-routes.js +1 -1
- package/dist/i18n/settings-hub-keys.js +1 -2
- package/dist/internal/stack-support.d.ts +12 -0
- package/dist/internal/stack-support.js +12 -0
- package/dist/internal/testing.d.ts +4 -0
- package/dist/internal/testing.js +5 -0
- package/dist/jobs/job-runner.js +23 -11
- package/dist/migrations/pending-rebuilds.js +1 -1
- package/dist/migrations/pii-event-backfill.js +2 -2
- package/dist/pipeline/dispatch-shared.js +43 -23
- package/dist/pipeline/dispatch-write.js +6 -2
- package/dist/pipeline/dispatcher-utils.js +6 -2
- package/dist/pipeline/event-consumer-state.js +1 -1
- package/dist/pipeline/projection-rebuild.d.ts +1 -0
- package/dist/pipeline/projection-rebuild.js +1 -1
- package/dist/pipeline/projection-state.js +1 -1
- package/dist/pipeline/system-hooks.js +1 -1
- package/dist/schema-cli.js +3 -3
- package/dist/search/purge-subject.js +20 -10
- package/dist/seeding/index.d.ts +1 -0
- package/dist/seeding/index.js +1 -0
- package/dist/stack/index.d.ts +10 -9
- package/dist/stack/index.js +11 -19
- package/dist/testing/e2e-generator.d.ts +5 -49
- package/dist/testing/e2e-generator.js +5 -382
- package/dist/testing/handler-context.d.ts +5 -4
- package/dist/testing/handler-context.js +5 -69
- package/dist/testing/index.d.ts +10 -26
- package/dist/testing/index.js +10 -34
- package/dist/testing/real-providers.d.ts +5 -3
- package/dist/testing/real-providers.js +5 -21
- package/dist/upgrade-cli.js +6 -4
- package/package.json +37 -9
- package/src/changes.json +64 -0
- package/src/scripts/codemod/README.md +43 -0
- package/src/scripts/codemod/codemod-support.ts +276 -0
- package/src/scripts/codemod/db-factory.ts +810 -0
- package/src/scripts/codemod/feature-factory.ts +429 -0
- package/src/scripts/codemod/s3-imports.ts +274 -0
- package/src/scripts/codemod/tenant-db-options.ts +454 -0
- package/src/scripts/codemod/testing-move-targets.ts +161 -0
- package/src/scripts/codemod/testing-move.ts +520 -0
- package/dist/bun-db/connection.d.ts +0 -17
- package/dist/bun-db/connection.js +0 -40
- package/dist/bun-db/index.d.ts +0 -4
- package/dist/bun-db/query.d.ts +0 -106
- package/dist/bun-db/query.js +0 -1457
- package/dist/db/api.d.ts +0 -19
- package/dist/db/api.js +0 -23
- package/dist/db/bun-provider.d.ts +0 -2
- package/dist/db/bun-provider.js +0 -28
- package/dist/db/postgres-provider.d.ts +0 -2
- package/dist/db/postgres-provider.js +0 -21
- package/dist/db/query-api.d.ts +0 -2
- package/dist/db/query-api.js +0 -6
- package/dist/stack/db.d.ts +0 -22
- package/dist/stack/db.js +0 -67
- package/dist/stack/drain-event-consumers.d.ts +0 -4
- package/dist/stack/drain-event-consumers.js +0 -51
- package/dist/stack/drain-jobs.d.ts +0 -22
- package/dist/stack/drain-jobs.js +0 -141
- package/dist/stack/event-collector.d.ts +0 -9
- package/dist/stack/event-collector.js +0 -12
- package/dist/stack/push-entity-projection-tables.d.ts +0 -24
- package/dist/stack/push-entity-projection-tables.js +0 -74
- package/dist/stack/redis.d.ts +0 -17
- package/dist/stack/redis.js +0 -52
- package/dist/stack/request-helper.d.ts +0 -35
- package/dist/stack/request-helper.js +0 -144
- package/dist/stack/test-stack.d.ts +0 -192
- package/dist/stack/test-stack.js +0 -429
- package/dist/testing/access-assertions.d.ts +0 -2
- package/dist/testing/access-assertions.js +0 -13
- package/dist/testing/assertions.d.ts +0 -10
- package/dist/testing/assertions.js +0 -22
- package/dist/testing/boot-validator-fixture.d.ts +0 -3
- package/dist/testing/boot-validator-fixture.js +0 -101
- package/dist/testing/closed-connection-error.d.ts +0 -7
- package/dist/testing/closed-connection-error.js +0 -46
- package/dist/testing/db-cleanup.d.ts +0 -9
- package/dist/testing/db-cleanup.js +0 -31
- package/dist/testing/expect-error.d.ts +0 -2
- package/dist/testing/expect-error.js +0 -22
- package/dist/testing/file-provider-contract.d.ts +0 -2
- package/dist/testing/file-provider-contract.js +0 -102
- package/dist/testing/http-cookies.d.ts +0 -7
- package/dist/testing/http-cookies.js +0 -47
- package/dist/testing/late-bound.d.ts +0 -9
- package/dist/testing/late-bound.js +0 -31
- package/dist/testing/manual-clock.d.ts +0 -14
- package/dist/testing/manual-clock.js +0 -12
- package/dist/testing/multipart-helper.d.ts +0 -22
- package/dist/testing/multipart-helper.js +0 -77
- package/dist/testing/mutable-master-key-provider.d.ts +0 -7
- package/dist/testing/mutable-master-key-provider.js +0 -35
- package/dist/testing/observability-recorder.d.ts +0 -9
- package/dist/testing/observability-recorder.js +0 -29
- package/dist/testing/seed.d.ts +0 -9
- package/dist/testing/seed.js +0 -21
- package/dist/testing/shared-entities.d.ts +0 -286
- package/dist/testing/shared-entities.js +0 -56
- package/dist/testing/utils.d.ts +0 -1
- package/dist/testing/utils.js +0 -1
- package/dist/testing/wait-for.d.ts +0 -20
- package/dist/testing/wait-for.js +0 -47
- package/dist/testing/without-ambient-temporal.d.ts +0 -5
- package/dist/testing/without-ambient-temporal.js +0 -17
- package/dist/testing/zip-fixture.d.ts +0 -1
- package/dist/testing/zip-fixture.js +0 -33
- /package/dist/{stack/test-users.d.ts → seeding/seed-users.d.ts} +0 -0
- /package/dist/{stack/test-users.js → seeding/seed-users.js} +0 -0
|
@@ -96,8 +96,8 @@ Six integration tests pin everything at once:
|
|
|
96
96
|
// - getAllProjectionProgress (projection lag for ops dashboards)
|
|
97
97
|
|
|
98
98
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
99
|
-
import { insertOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
100
99
|
import type { DbTx } from "@cosmicdrift/kumiko-framework/db";
|
|
100
|
+
import { insertOne, selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
101
101
|
import {
|
|
102
102
|
append,
|
|
103
103
|
loadAggregate as loadAggregateRaw,
|
|
@@ -106,14 +106,14 @@ import {
|
|
|
106
106
|
upcastStoredEvent,
|
|
107
107
|
} from "@cosmicdrift/kumiko-framework/event-store";
|
|
108
108
|
import { getAllProjectionProgress } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
109
|
+
import { createTestUser } from "@cosmicdrift/kumiko-framework/seeding";
|
|
109
110
|
import {
|
|
110
|
-
createTestUser,
|
|
111
111
|
resetEventStore,
|
|
112
112
|
setupTestStack,
|
|
113
113
|
type TestStack,
|
|
114
114
|
unsafeCreateEntityTable,
|
|
115
115
|
unsafePushTables,
|
|
116
|
-
} from "@cosmicdrift/kumiko-
|
|
116
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
117
117
|
import {
|
|
118
118
|
approverDirectoryTable,
|
|
119
119
|
invoiceDetailTable,
|
|
@@ -831,8 +831,8 @@ describe("role-restricted write handlers", () => {
|
|
|
831
831
|
//
|
|
832
832
|
// - r.defineEvent with { version, migrations } — evolving event shapes +
|
|
833
833
|
// upcast older payloads on read (declarative + sync/async transforms)
|
|
834
|
-
// -
|
|
835
|
-
// -
|
|
834
|
+
// - emitEvent — write domain events onto the aggregate stream, payload typed by the EventDef
|
|
835
|
+
// - emitEvent with headers — Marten free key/value metadata
|
|
836
836
|
// - r.projection — single-stream read model (inline in the write TX)
|
|
837
837
|
// - r.multiStreamProjection — cross-aggregate async read model
|
|
838
838
|
// - ctx.loadAggregate with { asOf } — point-in-time aggregate state
|
|
@@ -845,26 +845,25 @@ describe("role-restricted write handlers", () => {
|
|
|
845
845
|
// - getAllProjectionProgress — projection lag for ops dashboards
|
|
846
846
|
|
|
847
847
|
import {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
defineFeature,
|
|
851
|
-
defineProjectionQueryHandler,
|
|
852
|
-
typedPayload,
|
|
853
|
-
} from "@app/define";
|
|
854
|
-
import {
|
|
848
|
+
buildEntityTable,
|
|
849
|
+
createEventStoreExecutor,
|
|
855
850
|
fetchOne,
|
|
856
851
|
incrementCounter,
|
|
857
852
|
insertOne,
|
|
858
|
-
updateMany,
|
|
859
|
-
} from "@cosmicdrift/kumiko-framework/bun-db";
|
|
860
|
-
import {
|
|
861
|
-
buildEntityTable,
|
|
862
|
-
createEventStoreExecutor,
|
|
863
853
|
integer,
|
|
864
854
|
table,
|
|
865
855
|
text,
|
|
856
|
+
updateMany,
|
|
866
857
|
uuid,
|
|
867
858
|
} from "@cosmicdrift/kumiko-framework/db";
|
|
859
|
+
import {
|
|
860
|
+
createEntity,
|
|
861
|
+
createTextField,
|
|
862
|
+
defineFeature,
|
|
863
|
+
defineProjectionQueryHandler,
|
|
864
|
+
emitEvent,
|
|
865
|
+
typedPayload,
|
|
866
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
868
867
|
import { z } from "zod";
|
|
869
868
|
|
|
870
869
|
// --- Reducer: shared by live + snapshot-aware query handlers ---
|
|
@@ -1112,10 +1111,9 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
1112
1111
|
const headers: Record<string, string> = {};
|
|
1113
1112
|
if (event.payload.geoRegion) headers["geoRegion"] = event.payload.geoRegion;
|
|
1114
1113
|
if (event.payload.abTestBucket) headers["abTestBucket"] = event.payload.abTestBucket;
|
|
1115
|
-
await ctx
|
|
1114
|
+
await emitEvent(ctx, approved, {
|
|
1116
1115
|
aggregateId: event.payload.id,
|
|
1117
1116
|
aggregateType: "showcase-invoice",
|
|
1118
|
-
type: approved.name,
|
|
1119
1117
|
payload: { amountCents: event.payload.amountCents, approvedBy: event.payload.approvedBy },
|
|
1120
1118
|
...(Object.keys(headers).length > 0 ? { headers } : {}),
|
|
1121
1119
|
});
|
|
@@ -1132,10 +1130,9 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
1132
1130
|
approverId: event.payload.approverId,
|
|
1133
1131
|
});
|
|
1134
1132
|
const approverDisplayName: string = row?.displayName ?? `unknown:${event.payload.approverId}`;
|
|
1135
|
-
await ctx
|
|
1133
|
+
await emitEvent(ctx, acknowledged, {
|
|
1136
1134
|
aggregateId: event.payload.id,
|
|
1137
1135
|
aggregateType: "showcase-invoice",
|
|
1138
|
-
type: acknowledged.name,
|
|
1139
1136
|
payload: { approverId: event.payload.approverId, approverDisplayName },
|
|
1140
1137
|
});
|
|
1141
1138
|
return { isSuccess: true as const, data: { id: event.payload.id } };
|
|
@@ -1147,10 +1144,9 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
1147
1144
|
name: "invoice:pay",
|
|
1148
1145
|
schema: z.object({ id: z.uuid(), amountCents: z.number().int() }),
|
|
1149
1146
|
handler: async (event, ctx) => {
|
|
1150
|
-
await ctx
|
|
1147
|
+
await emitEvent(ctx, paid, {
|
|
1151
1148
|
aggregateId: event.payload.id,
|
|
1152
1149
|
aggregateType: "showcase-invoice",
|
|
1153
|
-
type: paid.name,
|
|
1154
1150
|
payload: { amountCents: event.payload.amountCents },
|
|
1155
1151
|
});
|
|
1156
1152
|
return { isSuccess: true as const, data: { id: event.payload.id } };
|
|
@@ -39,16 +39,16 @@ import {
|
|
|
39
39
|
GlobalFeatureToggleRuntime,
|
|
40
40
|
globalFeatureStateTable,
|
|
41
41
|
} from "@cosmicdrift/kumiko-bundled-features/feature-toggles";
|
|
42
|
-
import { selectMany } from "@cosmicdrift/kumiko-framework/
|
|
42
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
43
43
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
44
|
+
import { createTestUser } from "@cosmicdrift/kumiko-framework/seeding";
|
|
45
|
+
import { createLateBoundHolder } from "@cosmicdrift/kumiko-testing/helpers";
|
|
44
46
|
import {
|
|
45
|
-
createTestUser,
|
|
46
47
|
setupTestStack,
|
|
47
48
|
type TestStack,
|
|
48
49
|
unsafeCreateEntityTable,
|
|
49
50
|
unsafePushTables,
|
|
50
|
-
} from "@cosmicdrift/kumiko-
|
|
51
|
-
import { createLateBoundHolder } from "@cosmicdrift/kumiko-framework/testing";
|
|
51
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
52
52
|
import {
|
|
53
53
|
createProductAuditFeature,
|
|
54
54
|
createProductFeature,
|
|
@@ -89,14 +89,13 @@ Covers all three roles reading and writing, including the expected
|
|
|
89
89
|
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
90
90
|
import type { EntityId, SaveContext } from "@cosmicdrift/kumiko-framework/engine";
|
|
91
91
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
92
|
+
import { createTestUser, TestUsers } from "@cosmicdrift/kumiko-framework/seeding";
|
|
93
|
+
import { expectErrorIncludes } from "@cosmicdrift/kumiko-testing/helpers";
|
|
92
94
|
import {
|
|
93
|
-
createTestUser,
|
|
94
95
|
setupTestStack,
|
|
95
96
|
type TestStack,
|
|
96
|
-
TestUsers,
|
|
97
97
|
unsafeCreateEntityTable,
|
|
98
|
-
} from "@cosmicdrift/kumiko-
|
|
99
|
-
import { expectErrorIncludes } from "@cosmicdrift/kumiko-framework/testing";
|
|
98
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
100
99
|
import { employeeEntity, employeeFeature } from "../feature";
|
|
101
100
|
|
|
102
101
|
let stack: TestStack;
|
|
@@ -47,27 +47,26 @@ bun test
|
|
|
47
47
|
|
|
48
48
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
49
49
|
import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";
|
|
50
|
-
import {
|
|
50
|
+
import { createDerivativesSharpFeature } from "@cosmicdrift/kumiko-bundled-features/derivatives-sharp";
|
|
51
51
|
import { createFileDerivativesFeature } from "@cosmicdrift/kumiko-bundled-features/file-derivatives";
|
|
52
|
-
import {
|
|
53
|
-
|
|
54
|
-
import { entityEventName } from "@cosmicdrift/kumiko-framework/db";
|
|
52
|
+
import { createFileFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/file-foundation";
|
|
53
|
+
|
|
54
|
+
import { asRawClient, entityEventName } from "@cosmicdrift/kumiko-framework/db";
|
|
55
55
|
import {
|
|
56
56
|
createFilesFeature,
|
|
57
57
|
createInMemoryFileProvider,
|
|
58
58
|
type InMemoryFileProvider,
|
|
59
59
|
} from "@cosmicdrift/kumiko-framework/files";
|
|
60
|
+
import { createTestUser, testTenantId } from "@cosmicdrift/kumiko-framework/seeding";
|
|
61
|
+
import {
|
|
62
|
+
buildMultipartBody,
|
|
63
|
+
patchFileInstanceofForBunTest,
|
|
64
|
+
} from "@cosmicdrift/kumiko-testing/helpers";
|
|
60
65
|
import {
|
|
61
|
-
createTestUser,
|
|
62
66
|
setupTestStack,
|
|
63
67
|
type TestStack,
|
|
64
|
-
testTenantId,
|
|
65
68
|
unsafeCreateEntityTable,
|
|
66
|
-
} from "@cosmicdrift/kumiko-
|
|
67
|
-
import {
|
|
68
|
-
buildMultipartBody,
|
|
69
|
-
patchFileInstanceofForBunTest,
|
|
70
|
-
} from "@cosmicdrift/kumiko-framework/testing";
|
|
69
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
71
70
|
import sharp from "sharp";
|
|
72
71
|
import { coverOrder, filesPostProcessingFeature, photoEntity, publishedPhotoIds } from "../feature";
|
|
73
72
|
|
|
@@ -97,7 +96,7 @@ beforeAll(async () => {
|
|
|
97
96
|
stack = await setupTestStack({
|
|
98
97
|
features: [
|
|
99
98
|
createConfigFeature(),
|
|
100
|
-
|
|
99
|
+
createFileFoundationFeature(),
|
|
101
100
|
createFilesFeature(),
|
|
102
101
|
createFileDerivativesFeature({
|
|
103
102
|
resolveApexTenant: (host) => {
|
|
@@ -106,7 +105,7 @@ beforeAll(async () => {
|
|
|
106
105
|
return null;
|
|
107
106
|
},
|
|
108
107
|
}),
|
|
109
|
-
|
|
108
|
+
createDerivativesSharpFeature(),
|
|
110
109
|
filesPostProcessingFeature,
|
|
111
110
|
],
|
|
112
111
|
files: { storageProvider: provider },
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<!-- Generated by `bun run gen:agent-docs` from samples/recipes/golden-path. Do not edit. -->
|
|
2
|
+
|
|
3
|
+
# golden-path
|
|
4
|
+
|
|
5
|
+
The smallest complete feature: entity, write handler, event, projection, screen and an integration test.
|
|
6
|
+
|
|
7
|
+
## README
|
|
8
|
+
|
|
9
|
+
# Golden path
|
|
10
|
+
|
|
11
|
+
The smallest complete Kumiko feature: one entity, one write handler that emits
|
|
12
|
+
an event, one projection, one screen, and an integration test that writes
|
|
13
|
+
through HTTP and reads the result back. Every other recipe is a variation on
|
|
14
|
+
this shape. The walkthrough is in [`docs/golden-path.md`](../../../docs/golden-path.md).
|
|
15
|
+
|
|
16
|
+
## What it shows
|
|
17
|
+
|
|
18
|
+
- **Entity** — `ticketEntity` declares `title` and `isClosed`. `r.crud` registers
|
|
19
|
+
`ticket:create`, `ticket:list` and `ticket:detail` for it; update and delete
|
|
20
|
+
are switched off with `verbs`.
|
|
21
|
+
- **Write handler** — `ticket:close` checks that the ticket exists and emits the
|
|
22
|
+
`ticket-closed` event. It never touches the table.
|
|
23
|
+
- **Event** — `r.defineEvent` declares `ticket-closed` with a Zod payload;
|
|
24
|
+
`emitEvent` appends it to the ticket's stream in the handler's transaction.
|
|
25
|
+
- **Projection** — `ticket-status` folds `ticket-closed` into the ticket row
|
|
26
|
+
(`isClosed: true`), in the same transaction as the write.
|
|
27
|
+
- **Screen** — `ticket-list` is a declarative `entityList`; the renderer builds
|
|
28
|
+
the UI from it.
|
|
29
|
+
- **Test** — `setupTestStack` boots the real server and database;
|
|
30
|
+
`stack.http.writeOk` and `queryOk` go over HTTP. Each test runs in its own
|
|
31
|
+
tenant.
|
|
32
|
+
|
|
33
|
+
## Files
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
src/feature.ts entity, event, handler, projection, screen
|
|
37
|
+
src/__tests__/feature.integration.test.ts write → read back, access, not found
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Source files
|
|
41
|
+
|
|
42
|
+
### src/__tests__/feature.integration.test.ts
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
46
|
+
import { createTestUser, testTenantId } from "@cosmicdrift/kumiko-framework/seeding";
|
|
47
|
+
import { setupTestStack, type TestStack } from "@cosmicdrift/kumiko-testing/stack";
|
|
48
|
+
import { helpdeskFeature } from "../feature";
|
|
49
|
+
|
|
50
|
+
type TicketRow = { id: string; title: string; isClosed: boolean };
|
|
51
|
+
|
|
52
|
+
let stack: TestStack;
|
|
53
|
+
let nextTenant = 1;
|
|
54
|
+
let admin = createTestUser({ roles: ["Admin"], tenantId: testTenantId(nextTenant) });
|
|
55
|
+
let agent = createTestUser({ id: 2, roles: ["User"], tenantId: testTenantId(nextTenant) });
|
|
56
|
+
|
|
57
|
+
beforeAll(async () => {
|
|
58
|
+
stack = await setupTestStack({ features: [helpdeskFeature], systemHooks: [] });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterAll(async () => {
|
|
62
|
+
await stack.cleanup();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// A fresh tenant per test keeps rows from other tests out of every list.
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
nextTenant += 1;
|
|
68
|
+
admin = createTestUser({ roles: ["Admin"], tenantId: testTenantId(nextTenant) });
|
|
69
|
+
agent = createTestUser({ id: 2, roles: ["User"], tenantId: testTenantId(nextTenant) });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
async function openTicket(title: string): Promise<string> {
|
|
73
|
+
const created = await stack.http.writeOk<{ id: string }>(
|
|
74
|
+
"helpdesk:write:ticket:create",
|
|
75
|
+
{ title },
|
|
76
|
+
agent,
|
|
77
|
+
);
|
|
78
|
+
return created.id;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function readTicket(id: string): Promise<TicketRow> {
|
|
82
|
+
return stack.http.queryOk<TicketRow>("helpdesk:query:ticket:detail", { id }, agent);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe("golden path: ticket", () => {
|
|
86
|
+
test("a new ticket starts open", async () => {
|
|
87
|
+
const id = await openTicket("Printer is on fire");
|
|
88
|
+
|
|
89
|
+
expect(await readTicket(id)).toMatchObject({ title: "Printer is on fire", isClosed: false });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("closing a ticket emits an event that the projection folds into the read model", async () => {
|
|
93
|
+
const id = await openTicket("Login fails");
|
|
94
|
+
|
|
95
|
+
await stack.http.writeOk(
|
|
96
|
+
"helpdesk:write:ticket:close",
|
|
97
|
+
{ id, resolution: "Password reset" },
|
|
98
|
+
admin,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
expect(await readTicket(id)).toMatchObject({ title: "Login fails", isClosed: true });
|
|
102
|
+
const list = await stack.http.queryOk<{ rows: TicketRow[] }>(
|
|
103
|
+
"helpdesk:query:ticket:list",
|
|
104
|
+
{ filter: { field: "isClosed", op: "eq", value: true } },
|
|
105
|
+
agent,
|
|
106
|
+
);
|
|
107
|
+
expect(list.rows.map((row) => row.id)).toEqual([id]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("only an Admin may close a ticket", async () => {
|
|
111
|
+
const id = await openTicket("Slow wifi");
|
|
112
|
+
|
|
113
|
+
const denied = await stack.http.writeErr(
|
|
114
|
+
"helpdesk:write:ticket:close",
|
|
115
|
+
{ id, resolution: "Moved the router" },
|
|
116
|
+
agent,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(denied.code).toBe("access_denied");
|
|
120
|
+
expect((await readTicket(id)).isClosed).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("closing an unknown ticket fails with not_found", async () => {
|
|
124
|
+
const missing = await stack.http.writeErr(
|
|
125
|
+
"helpdesk:write:ticket:close",
|
|
126
|
+
{ id: "99999999-9999-4999-8999-999999999999", resolution: "n/a" },
|
|
127
|
+
admin,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
expect(missing.code).toBe("not_found");
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### src/feature.ts
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import {
|
|
139
|
+
createBooleanField,
|
|
140
|
+
createEntity,
|
|
141
|
+
createEntityExecutor,
|
|
142
|
+
createTextField,
|
|
143
|
+
defineFeature,
|
|
144
|
+
emitEvent,
|
|
145
|
+
setFields,
|
|
146
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
147
|
+
import { failNotFound } from "@cosmicdrift/kumiko-framework/errors";
|
|
148
|
+
import { z } from "zod";
|
|
149
|
+
|
|
150
|
+
export const ticketEntity = createEntity({
|
|
151
|
+
table: "read_golden_path_tickets",
|
|
152
|
+
fields: {
|
|
153
|
+
title: createTextField({ personal: false, reason: "is_business_data", required: true }),
|
|
154
|
+
isClosed: createBooleanField({ default: false }),
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
export const helpdeskFeature = defineFeature("helpdesk", (r) => {
|
|
159
|
+
const { table: ticketTable, executor: ticketExecutor } = createEntityExecutor(
|
|
160
|
+
"ticket",
|
|
161
|
+
ticketEntity,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
r.crud("ticket", ticketEntity, {
|
|
165
|
+
write: { access: { roles: ["Admin", "User"] } },
|
|
166
|
+
read: { access: { roles: ["Admin", "User"] } },
|
|
167
|
+
verbs: { update: false, delete: false },
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const ticketClosed = r.defineEvent("ticket-closed", z.object({ resolution: z.string().min(1) }), {
|
|
171
|
+
piiFields: "none",
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
r.writeHandler({
|
|
175
|
+
name: "ticket:close",
|
|
176
|
+
schema: z.object({ id: z.uuid(), resolution: z.string().min(1) }),
|
|
177
|
+
access: { roles: ["Admin"] },
|
|
178
|
+
handler: async (event, ctx) => {
|
|
179
|
+
const ticket = await ticketExecutor.detail({ id: event.payload.id }, event.user, ctx.db);
|
|
180
|
+
if (!ticket) {
|
|
181
|
+
return failNotFound("ticket", event.payload.id);
|
|
182
|
+
}
|
|
183
|
+
await emitEvent(ctx, ticketClosed, {
|
|
184
|
+
aggregateId: event.payload.id,
|
|
185
|
+
aggregateType: "ticket",
|
|
186
|
+
payload: { resolution: event.payload.resolution },
|
|
187
|
+
});
|
|
188
|
+
return { isSuccess: true as const, data: { id: event.payload.id } };
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
r.projection({
|
|
193
|
+
name: "ticket-status",
|
|
194
|
+
source: "ticket",
|
|
195
|
+
table: ticketTable,
|
|
196
|
+
apply: {
|
|
197
|
+
[ticketClosed.name]: setFields(ticketTable, { isClosed: true }),
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
r.screen({
|
|
202
|
+
id: "ticket-list",
|
|
203
|
+
type: "entityList",
|
|
204
|
+
entity: "ticket",
|
|
205
|
+
columns: ["title", "isClosed"],
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
```
|
|
@@ -39,12 +39,12 @@ bun test
|
|
|
39
39
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
40
40
|
import type { SaveContext } from "@cosmicdrift/kumiko-framework/engine";
|
|
41
41
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
42
|
+
import { createTestUser } from "@cosmicdrift/kumiko-framework/seeding";
|
|
42
43
|
import {
|
|
43
|
-
createTestUser,
|
|
44
44
|
setupTestStack,
|
|
45
45
|
type TestStack,
|
|
46
46
|
unsafeCreateEntityTable,
|
|
47
|
-
} from "@cosmicdrift/kumiko-
|
|
47
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
48
48
|
import { orderEntity, orderFeature } from "../feature";
|
|
49
49
|
|
|
50
50
|
let stack: TestStack;
|
|
@@ -63,15 +63,15 @@ import {
|
|
|
63
63
|
} from "@cosmicdrift/kumiko-bundled-features/compliance-profiles";
|
|
64
64
|
import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";
|
|
65
65
|
import {
|
|
66
|
+
createInboundMailFoundationFeature,
|
|
66
67
|
InboundMailFoundationHandlers,
|
|
67
|
-
inboundMailFoundationFeature,
|
|
68
68
|
inboundMessageEntity,
|
|
69
69
|
mailAccountEntity,
|
|
70
70
|
mailThreadEntity,
|
|
71
71
|
seenMessageEntity,
|
|
72
72
|
syncCursorEntity,
|
|
73
73
|
} from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
74
|
-
import {
|
|
74
|
+
import { createInboundProviderInMemoryFeature } from "@cosmicdrift/kumiko-bundled-features/inbound-provider-inmemory";
|
|
75
75
|
import { createTenantFeature, tenantEntity } from "@cosmicdrift/kumiko-bundled-features/tenant";
|
|
76
76
|
import { createTenantLifecycleFeature } from "@cosmicdrift/kumiko-bundled-features/tenant-lifecycle";
|
|
77
77
|
import { createRegistry } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -81,15 +81,15 @@ import {
|
|
|
81
81
|
createEventsTable,
|
|
82
82
|
} from "@cosmicdrift/kumiko-framework/event-store";
|
|
83
83
|
import { createEventConsumerStateTable } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
84
|
+
import { TestUsers } from "@cosmicdrift/kumiko-framework/seeding";
|
|
85
|
+
import { waitFor } from "@cosmicdrift/kumiko-testing/helpers";
|
|
84
86
|
import {
|
|
85
87
|
createTestDb,
|
|
86
88
|
createTestRedis,
|
|
87
89
|
type TestDb,
|
|
88
90
|
type TestRedis,
|
|
89
|
-
TestUsers,
|
|
90
91
|
unsafeCreateEntityTable,
|
|
91
|
-
} from "@cosmicdrift/kumiko-
|
|
92
|
-
import { waitFor } from "@cosmicdrift/kumiko-framework/testing";
|
|
92
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
93
93
|
import { createMailTriageFeature, triageInbox } from "../feature";
|
|
94
94
|
|
|
95
95
|
const JWT = "inbound-mail-triage-secret-minimum-32-chars!";
|
|
@@ -147,8 +147,8 @@ describe("inbound-mail triage sample", () => {
|
|
|
147
147
|
createTenantFeature(),
|
|
148
148
|
createComplianceProfilesFeature(),
|
|
149
149
|
createTenantLifecycleFeature(),
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
createInboundMailFoundationFeature(),
|
|
151
|
+
createInboundProviderInMemoryFeature(),
|
|
152
152
|
createMailTriageFeature(),
|
|
153
153
|
]);
|
|
154
154
|
const redisUrl = `redis://${testRedis.redis.options.host}:${testRedis.redis.options.port}/${testRedis.redis.options.db}`;
|
|
@@ -57,14 +57,14 @@ import {
|
|
|
57
57
|
createEventsTable,
|
|
58
58
|
} from "@cosmicdrift/kumiko-framework/event-store";
|
|
59
59
|
import { createEventConsumerStateTable } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
60
|
+
import { TestUsers } from "@cosmicdrift/kumiko-framework/seeding";
|
|
61
|
+
import { waitFor } from "@cosmicdrift/kumiko-testing/helpers";
|
|
60
62
|
import {
|
|
61
63
|
createTestDb,
|
|
62
64
|
createTestRedis,
|
|
63
65
|
type TestDb,
|
|
64
66
|
type TestRedis,
|
|
65
|
-
|
|
66
|
-
} from "@cosmicdrift/kumiko-framework/stack";
|
|
67
|
-
import { waitFor } from "@cosmicdrift/kumiko-framework/testing";
|
|
67
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
68
68
|
import { createLaneRoutingFeature, renderedReceipts, sentConfirmations } from "../feature";
|
|
69
69
|
|
|
70
70
|
const JWT = "lane-routing-sample-secret-minimum-32-chars!";
|
|
@@ -73,13 +73,12 @@ import {
|
|
|
73
73
|
transactionEntity,
|
|
74
74
|
} from "@cosmicdrift/kumiko-bundled-features/ledger";
|
|
75
75
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
76
|
+
import { createTestUser, testTenantId } from "@cosmicdrift/kumiko-framework/seeding";
|
|
76
77
|
import {
|
|
77
|
-
createTestUser,
|
|
78
78
|
setupTestStack,
|
|
79
79
|
type TestStack,
|
|
80
|
-
testTenantId,
|
|
81
80
|
unsafeCreateEntityTable,
|
|
82
|
-
} from "@cosmicdrift/kumiko-
|
|
81
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
83
82
|
import { bankingFlow, type LedgerClient } from "../usage";
|
|
84
83
|
|
|
85
84
|
// Each test runs in its own tenant, so rows from other tests never show up.
|
|
@@ -82,13 +82,12 @@ import {
|
|
|
82
82
|
transactionEntity,
|
|
83
83
|
} from "@cosmicdrift/kumiko-bundled-features/ledger";
|
|
84
84
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
85
|
+
import { createTestUser, testTenantId } from "@cosmicdrift/kumiko-framework/seeding";
|
|
85
86
|
import {
|
|
86
|
-
createTestUser,
|
|
87
87
|
setupTestStack,
|
|
88
88
|
type TestStack,
|
|
89
|
-
testTenantId,
|
|
90
89
|
unsafeCreateEntityTable,
|
|
91
|
-
} from "@cosmicdrift/kumiko-
|
|
90
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
92
91
|
import { invoicingFlow, type LedgerClient } from "../usage";
|
|
93
92
|
|
|
94
93
|
// Each test runs in its own tenant, so rows from other tests never show up.
|
|
@@ -228,7 +228,7 @@ import {
|
|
|
228
228
|
setupTestStack,
|
|
229
229
|
type TestStack,
|
|
230
230
|
unsafeCreateEntityTable,
|
|
231
|
-
} from "@cosmicdrift/kumiko-
|
|
231
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
232
232
|
import { LEGAL_ROUTES, legalPagesFeature, templateResolverFeature } from "../feature";
|
|
233
233
|
|
|
234
234
|
let stack: TestStack;
|
|
@@ -64,13 +64,13 @@ bun kumiko test integration samples/lifecycle-hooks
|
|
|
64
64
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
65
65
|
import type { SaveContext } from "@cosmicdrift/kumiko-framework/engine";
|
|
66
66
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
67
|
+
import { createTestUser } from "@cosmicdrift/kumiko-framework/seeding";
|
|
68
|
+
import { expectErrorIncludes } from "@cosmicdrift/kumiko-testing/helpers";
|
|
67
69
|
import {
|
|
68
|
-
createTestUser,
|
|
69
70
|
setupTestStack,
|
|
70
71
|
type TestStack,
|
|
71
72
|
unsafeCreateEntityTable,
|
|
72
|
-
} from "@cosmicdrift/kumiko-
|
|
73
|
-
import { expectErrorIncludes } from "@cosmicdrift/kumiko-framework/testing";
|
|
73
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
74
74
|
import { articleEntity, articlesFeature, hookLog } from "../feature";
|
|
75
75
|
|
|
76
76
|
let stack: TestStack;
|
|
@@ -169,7 +169,7 @@ import {
|
|
|
169
169
|
createSecretsFeature,
|
|
170
170
|
tenantSecretsTable,
|
|
171
171
|
} from "@cosmicdrift/kumiko-bundled-features/secrets";
|
|
172
|
-
import { selectMany } from "@cosmicdrift/kumiko-framework/
|
|
172
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
173
173
|
import {
|
|
174
174
|
type ConfigCascade,
|
|
175
175
|
createRegistry,
|
|
@@ -177,13 +177,12 @@ import {
|
|
|
177
177
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
178
178
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
179
179
|
import { createEnvMasterKeyProvider } from "@cosmicdrift/kumiko-framework/secrets";
|
|
180
|
+
import { TestUsers, testTenantId } from "@cosmicdrift/kumiko-framework/seeding";
|
|
180
181
|
import {
|
|
181
182
|
setupTestStack,
|
|
182
183
|
type TestStack,
|
|
183
|
-
TestUsers,
|
|
184
|
-
testTenantId,
|
|
185
184
|
unsafePushTables,
|
|
186
|
-
} from "@cosmicdrift/kumiko-
|
|
185
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
187
186
|
import { integrationsFeature, paymentApiKeyHandle, smtpHostHandle } from "../feature";
|
|
188
187
|
|
|
189
188
|
const TENANT_A = testTenantId(1);
|
|
@@ -180,13 +180,13 @@ import { BRANDING_QN, pageEntity } from "@cosmicdrift/kumiko-bundled-features/ma
|
|
|
180
180
|
import { seedPage } from "@cosmicdrift/kumiko-bundled-features/managed-pages/seeding";
|
|
181
181
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
182
182
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
183
|
+
import { createTestUser } from "@cosmicdrift/kumiko-framework/seeding";
|
|
183
184
|
import {
|
|
184
|
-
createTestUser,
|
|
185
185
|
setupTestStack,
|
|
186
186
|
type TestStack,
|
|
187
187
|
unsafeCreateEntityTable,
|
|
188
188
|
unsafePushTables,
|
|
189
|
-
} from "@cosmicdrift/kumiko-
|
|
189
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
190
190
|
import { configFeature, managedPagesCssFeature, managedPagesFeature } from "../feature";
|
|
191
191
|
|
|
192
192
|
// Branding writes are tenant-scoped — the admin's tenantId must equal the
|
|
@@ -117,18 +117,17 @@ bun test src/__tests__/feature.integration.test.ts
|
|
|
117
117
|
|
|
118
118
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
119
119
|
import {
|
|
120
|
+
createNotesHistoryFeature,
|
|
120
121
|
noteEntryEntity,
|
|
121
122
|
noteMentionEntity,
|
|
122
|
-
notesHistoryFeature,
|
|
123
123
|
} from "@cosmicdrift/kumiko-bundled-features/notes-history";
|
|
124
124
|
import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
|
|
125
|
+
import { createTestUser, testTenantId } from "@cosmicdrift/kumiko-framework/seeding";
|
|
125
126
|
import {
|
|
126
|
-
createTestUser,
|
|
127
127
|
setupTestStack,
|
|
128
128
|
type TestStack,
|
|
129
|
-
testTenantId,
|
|
130
129
|
unsafeCreateEntityTable,
|
|
131
|
-
} from "@cosmicdrift/kumiko-
|
|
130
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
132
131
|
import { taskEntity, taskFeature } from "../feature";
|
|
133
132
|
import { type NotesClient, notesFlow } from "../usage";
|
|
134
133
|
|
|
@@ -145,7 +144,7 @@ const notesClient: NotesClient = {
|
|
|
145
144
|
let stack: TestStack;
|
|
146
145
|
|
|
147
146
|
beforeAll(async () => {
|
|
148
|
-
stack = await setupTestStack({ features: [
|
|
147
|
+
stack = await setupTestStack({ features: [createNotesHistoryFeature(), taskFeature] });
|
|
149
148
|
await unsafeCreateEntityTable(stack.db, noteEntryEntity);
|
|
150
149
|
await unsafeCreateEntityTable(stack.db, noteMentionEntity);
|
|
151
150
|
await unsafeCreateEntityTable(stack.db, taskEntity);
|
|
@@ -40,15 +40,13 @@ bun test
|
|
|
40
40
|
|
|
41
41
|
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
42
42
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
43
|
+
import { createTestUser, TestUsers, testTenantId } from "@cosmicdrift/kumiko-framework/seeding";
|
|
44
|
+
import { expectErrorIncludes } from "@cosmicdrift/kumiko-testing/helpers";
|
|
43
45
|
import {
|
|
44
|
-
createTestUser,
|
|
45
46
|
setupTestStack,
|
|
46
47
|
type TestStack,
|
|
47
|
-
TestUsers,
|
|
48
|
-
testTenantId,
|
|
49
48
|
unsafeCreateEntityTable,
|
|
50
|
-
} from "@cosmicdrift/kumiko-
|
|
51
|
-
import { expectErrorIncludes } from "@cosmicdrift/kumiko-framework/testing";
|
|
49
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
52
50
|
import { contractEntity, contractsFeature, teamsFeature } from "../feature";
|
|
53
51
|
|
|
54
52
|
let stack: TestStack;
|
|
@@ -46,13 +46,12 @@ import {
|
|
|
46
46
|
createFilesFeature,
|
|
47
47
|
createInMemoryFileProvider,
|
|
48
48
|
} from "@cosmicdrift/kumiko-framework/files";
|
|
49
|
+
import { createTestUser, testTenantId } from "@cosmicdrift/kumiko-framework/seeding";
|
|
49
50
|
import {
|
|
50
|
-
createTestUser,
|
|
51
51
|
setupTestStack,
|
|
52
52
|
type TestStack,
|
|
53
|
-
testTenantId,
|
|
54
53
|
unsafeCreateEntityTable,
|
|
55
|
-
} from "@cosmicdrift/kumiko-
|
|
54
|
+
} from "@cosmicdrift/kumiko-testing/stack";
|
|
56
55
|
import { extractText, getDocumentProxy } from "unpdf";
|
|
57
56
|
import { inspectionEntity, pdfAuditExportFeature } from "../feature";
|
|
58
57
|
|