import { b8 as PluginDefinition, a as CollectionConfig, bb as SingleConfig, bc as FieldGroupConfig, gH as LocalizationConfig, L as Logger, gI as CollectionAccessRules, gJ as Nextly, gs as getService, p as HookRegistry, cP as EventBus } from './_dts-chunks/auth-service.d-B0csjDLa.d.ts'; import { b as createAdapter } from './_dts-chunks/factory.d-CjmgdkyX.d.ts'; import '@nextlyhq/adapter-drizzle'; import '@nextlyhq/adapter-drizzle/types'; import 'react'; import './_dts-chunks/nextly-error.d-WlStqaV9.d.ts'; import './_dts-chunks/error-codes.d-CbwkO1ux.d.ts'; import './_dts-chunks/media.d-DtIw8UQM.d.ts'; import 'zod'; import './_dts-chunks/storage.d-CEowrt6p.d.ts'; import 'drizzle-orm'; /** * `createTestNextly` — the integration harness. * * Boots a REAL Nextly instance (not mocks), running the full plugin lifecycle * (resolve → setup → schema sync → init), so plugin authors and the framework * can integration-test hooks, events, and lifecycle. Lives in core and is * re-exported from `@nextlyhq/plugin-sdk/testing`. * * In-memory SQLite by default, which needs no server and no cleanup. Pass * `dialect` to boot against PostgreSQL or MySQL instead: that instance gets a * database of its own, dropped when it is destroyed. Column types, default * expressions, and JSON handling differ enough between dialects that SQLite * alone will not reveal a defect in any of them. * * @module plugins/test-nextly */ type TestAdapter = Awaited>; /** The dialects a test instance can boot on. */ type TestDialect = "sqlite" | "postgresql" | "mysql"; /** * Which dialects this process is CONFIGURED to boot on. * * SQLite is always included — it runs in memory with no server. The others are * included when their server URL is set, so a suite can cover every dialect a * developer has configured without failing on the ones they have not. * * Configured is not the same as reachable, and this deliberately does not * probe: a URL pointing at a stopped container is still reported, and the * suite will fail against it rather than skip. That is the intended outcome — * a dialect someone asked for and cannot reach is a broken environment, and * silently skipping it is how the gaps this harness exists to close were * hidden in the first place. */ declare function getConfiguredTestDialects(): TestDialect[]; interface CreateTestNextlyOptions { /** * Boot against a real database server instead of in-memory SQLite. * * A dedicated database is created for this instance and dropped by * `destroy()`, because the shared test database is written to by every other * suite in the run and cannot answer a question about schema state. Requires * the dialect's server URL (`TEST_POSTGRES_URL` / `TEST_MYSQL_URL`) unless * `serverUrl` is given; check `getConfiguredTestDialects()` to skip * cleanly when it is not configured. * * Ignored when `adapter` is supplied: that adapter is used as given, and * nothing is provisioned or dropped for it. Note that `destroy()` still * disconnects it, as it always has — supplying an adapter chooses the * connection, not its lifetime. */ dialect?: TestDialect; /** * Server to create the throwaway database on, overriding the environment * variable for `dialect`. The database named in it is only used to connect. */ serverUrl?: string; /** Plugins to boot (their full lifecycle runs). */ plugins?: PluginDefinition[]; /** Code-first collections to register (tables created on the in-memory DB). */ collections?: CollectionConfig[]; /** Code-first singles. */ singles?: SingleConfig[]; /** Code-first field groups. */ fieldGroups?: FieldGroupConfig[]; /** Content-localization config (i18n). Normalized and wired so localized reads resolve. */ localization?: LocalizationConfig; /** Override the adapter (defaults to a fresh in-memory SQLite adapter). */ adapter?: TestAdapter; /** Override the logger (defaults to a near-silent test logger). */ logger?: Logger; /** * Stored per-collection access rules (`accessRules`) keyed by slug. Code-first * `defineCollection` carries only code `access` functions, so an integration * test that needs a STORED rule (for example an owner-only publish rule) sets * it here. After boot the rule is written to the collection's * `dynamic_collections` row exactly as the Schema Builder would persist it, so * the access path surfaces it through `getCollection`. */ collectionAccessRules?: Record; /** * Stored per-single access rules, keyed by slug. Mirrors * `collectionAccessRules` for Singles: written to the `dynamic_singles` row * after boot so the access path surfaces a STORED rule (for example an * owner-only publish rule) that a code-first `defineSingle` cannot carry. */ singleAccessRules?: Record; } interface TestNextly { /** * The booted direct-API facade for CRUD assertions. * * Resolved on access. Resolving it registers the `nextlyDirectAPI` container * binding as a side effect, and that binding is what a hook's `req.nextly` * comes from — so a test that reads this property before asserting anything * about `req.nextly` has supplied the answer itself. Assert the binding * first, then read this. */ nextly: Nextly; /** Container accessor for inspecting any registered service. */ getService: typeof getService; /** The live hook registry (assert hook registration/execution). */ hooks: HookRegistry; /** The live event bus (assert emissions; call `events.settle()`). */ events: EventBus; /** The underlying adapter (raw DB inspection). */ adapter: TestAdapter; /** Tear down: run plugin destroy (T9), disconnect, reset all singletons. */ destroy(): Promise; } /** * Boot a real, isolated Nextly instance on in-memory SQLite. * * Always call `await handle.destroy()` (e.g. in `afterEach`) so the next boot * starts clean — `registerServices` throws if services are already registered. */ declare function createTestNextly(opts?: CreateTestNextlyOptions): Promise; /** * Shared buffer and detection for PostgreSQL aborted-transaction sightings. * * Deliberately dependency-free, for two reasons. `src/__tests__/setup.ts` is the setup file for * BOTH the unit and the integration vitest configs, so anything it imports is loaded into every * unit test too — importing the harness there pulls in the DI registry, the adapters and the event * bus, which is enough to break unit suites that expect none of it. And this module ships through * `nextly/testing`, so it must not reach for a test framework: `expect` comes from vitest, which is * a devDependency and absent for consumers. * * That is why nothing here throws. `describeAbortedTransactions` returns the message and leaves the * assertion to the caller, so this package's own setup file and a plugin author's setup file can * each fail the test with their own runner while sharing one buffer, one detector and one message. * * @module plugins/aborted-transaction-sightings */ /** * PostgreSQL's SQLSTATE for "an earlier statement in this transaction failed". * * The authoritative signal. Unlike the message text this is fixed by the wire protocol, so it * survives a server running with a non-English `lc_messages` — where the human-readable text is * translated and an English substring match would silently never fire, leaving the suite falsely * green. */ declare const PG_ABORTED_TRANSACTION_SQLSTATE = "25P02"; /** * Whether a thrown value reports an aborted transaction. * * Walks the `cause` chain because the PostgreSQL adapter classifies the driver's error into a * `DatabaseError` and keeps the original underneath: the code can be on either one, and which * depends on where in the stack the value was caught. */ declare function isAbortedTransactionError(error: unknown): boolean; /** * Everything seen since the last read, clearing as it goes so one test's failure cannot be * re-reported against the next. */ declare function takeAbortedTransactionSightings(): string[]; /** * The failure message for anything recorded since the last check, or `null` when clean. * * Returns rather than throws so the caller's own test runner reports it: a thrown error from this * package would either depend on vitest or arrive as a `NextlyError`, whose public message is * deliberately generic and would hide the sightings behind "An unexpected error occurred." * * Consumes the buffer, so a failure is attributed to the test that caused it and never re-reported * against the next one. Use it from a per-test hook: * * ```ts * import { describeAbortedTransactions } from "nextly/testing"; * * afterEach(() => { * const aborted = describeAbortedTransactions(); * if (aborted) expect.fail(aborted); * }); * ``` */ declare function describeAbortedTransactions(): string | null; export { PG_ABORTED_TRANSACTION_SQLSTATE, createTestNextly, describeAbortedTransactions, getConfiguredTestDialects, isAbortedTransactionError, takeAbortedTransactionSightings }; export type { CreateTestNextlyOptions, TestDialect, TestNextly };