Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Add a testing utility for Evolu that uses in-memory SQLite #481

Closed
wants to merge 10 commits into from
5 changes: 4 additions & 1 deletion packages/evolu-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"build": "rm -rf dist ./README.md && tsc && cp ../../README.md ./",
"lint": "eslint src --ext .ts,.tsx",
"test": "vitest run",
"test:watch": "vitest",
"clean": "rm -rf .turbo node_modules dist",
"format": "prettier --write \"src/*.{ts,tsx,md}\"",
"protobuf": "pnpm protoc --ts_out ./src --proto_path protobuf protobuf/Protobuf.proto --ts_opt eslint_disable --ts_opt optimize_code_size && pnpm format"
Expand All @@ -58,14 +59,16 @@
"@protobuf-ts/runtime": "^2.9.4",
"@scure/bip39": "^1.3.0",
"kysely": "^0.27.3",
"nanoid": "^5.0.7"
"nanoid": "^5.0.7",
"better-sqlite3": "^11.0.0"
},
"devDependencies": {
"@effect/platform": "^0.64.0",
"@effect/schema": "^0.72.0",
"@evolu/tsconfig": "workspace:*",
"@protobuf-ts/plugin": "^2.9.4",
"@protobuf-ts/protoc": "^2.9.4",
"@types/better-sqlite3": "^7.6.10",
"array-shuffle": "^3.0.0",
"effect": "^3.2.1",
"eslint": "^8.57.0",
Expand Down
78 changes: 41 additions & 37 deletions packages/evolu-common/src/Evolu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ export class EvoluFactory extends Context.Tag("EvoluFactory")<
};
let evolu = instances.get(name);
if (evolu == null) {
evolu = createEvolu(
evolu = createEvoluEffect(
dbSchema,
runtime,
initialData as EvoluConfig["initialData"],
Expand Down Expand Up @@ -569,7 +569,7 @@ export interface EvoluConfig<T extends EvoluSchema = EvoluSchema>
mnemonic: Mnemonic;
}

const schemaToTables = (schema: S.Schema<any>) =>
export const schemaToTables = (schema: S.Schema<any>): Table[] =>
pipe(
getPropertySignatures(schema),
Record.toEntries,
Expand All @@ -596,12 +596,16 @@ const getPropertySignatures = <I extends { [K in keyof A]: any }, A>(
return out as any;
};

const createEvolu = (
export const createEvoluEffect = (
schema: DbSchema,
runtime: ManagedRuntime.ManagedRuntime<Config, never>,
initialData: EvoluConfig["initialData"],
mnemonic: Mnemonic | undefined,
) =>
initialData?: EvoluConfig["initialData"],
mnemonic?: Mnemonic,
): Effect.Effect<
Evolu<EvoluSchema>,
never,
Config | DbFactory | AppState | NanoIdGenerator | FlushSync
> =>
Effect.gen(function* () {
yield* Effect.logTrace("EvoluFactory createEvolu");
const config = yield* Config;
Expand Down Expand Up @@ -649,6 +653,37 @@ const createEvolu = (
);
};

const handlePatches =
(options?: {
/**
* The flushSync is for onComplete handlers only. For example, with
* React, when we want to focus on a node created by a mutation, we must
* ensure all DOM changes are flushed synchronously.
*/
readonly flushSync: boolean;
}) =>
(patches: ReadonlyArray<QueryPatches>) =>
Effect.logDebug(["Evolu handlePatches", { patches }]).pipe(
Effect.zipRight(rowsStoreStateFromPatches(patches)),
Effect.tap((nextState) =>
Effect.forEach(patches, ({ query }) =>
resolveLoadingPromises(
query,
nextState.get(query) || emptyRows(),
),
),
),
Effect.tap((nextState) => {
if (options?.flushSync) {
flushSync(() => {
rowsStore.setState(nextState).pipe(runSync);
});
} else {
rowsStore.setState(nextState).pipe(runSync);
}
}),
);

const handleDbReceive = () => {
Effect.gen(function* () {
yield* Effect.logTrace("Evolu handleDbReceive");
Expand Down Expand Up @@ -698,37 +733,6 @@ const createEvolu = (
reloadUrl: config.reloadUrl,
});

const handlePatches =
(options?: {
/**
* The flushSync is for onComplete handlers only. For example, with
* React, when we want to focus on a node created by a mutation, we must
* ensure all DOM changes are flushed synchronously.
*/
readonly flushSync: boolean;
}) =>
(patches: ReadonlyArray<QueryPatches>) =>
Effect.logDebug(["Evolu handlePatches", { patches }]).pipe(
Effect.zipRight(rowsStoreStateFromPatches(patches)),
Effect.tap((nextState) =>
Effect.forEach(patches, ({ query }) =>
resolveLoadingPromises(
query,
nextState.get(query) || emptyRows(),
),
),
),
Effect.tap((nextState) => {
if (options?.flushSync) {
flushSync(() => {
rowsStore.setState(nextState).pipe(runSync);
});
} else {
rowsStore.setState(nextState).pipe(runSync);
}
}),
);

const rowsStoreStateFromPatches = (patches: ReadonlyArray<QueryPatches>) =>
Effect.sync((): QueryRowsMap => {
const rowsStoreState = rowsStore.getState();
Expand Down
Loading
Loading