Skip to content

Commit 0f3e430

Browse files
syncing web worker logic
1 parent 594a5bb commit 0f3e430

7 files changed

Lines changed: 144 additions & 12 deletions

File tree

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { Layer, ManagedRuntime } from "effect";
22
import { ApiClient } from "./api-client";
33
import { Dexie } from "./dexie";
4+
import { TempWorkspace } from "./services/temp-workspace";
45
import { WorkspaceManager } from "./services/workspace-manager";
56

67
const MainLayer = Layer.mergeAll(
78
Dexie.Default,
89
ApiClient.Default,
9-
WorkspaceManager.Default
10+
WorkspaceManager.Default,
11+
TempWorkspace.Default
1012
);
1113

1214
export const RuntimeClient = ManagedRuntime.make(MainLayer);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Effect, Schema } from "effect";
2+
import { Dexie } from "../dexie";
3+
import { TempWorkspaceTable } from "../schema";
4+
5+
export class TempWorkspace extends Effect.Service<TempWorkspace>()(
6+
"TempWorkspace",
7+
{
8+
dependencies: [Dexie.Default],
9+
effect: Effect.gen(function* () {
10+
const { query } = yield* Dexie;
11+
return {
12+
put: (params: typeof TempWorkspaceTable.Type) =>
13+
Schema.encode(TempWorkspaceTable)(params).pipe(
14+
Effect.flatMap((data) => query((_) => _.temp_workspace.put(data)))
15+
),
16+
17+
get: ({ workspaceId }: { workspaceId: string }) =>
18+
query((_) =>
19+
_.temp_workspace
20+
.where("workspaceId")
21+
.equals(workspaceId)
22+
.limit(1)
23+
.first()
24+
).pipe(
25+
Effect.flatMap(Effect.fromNullable),
26+
Effect.flatMap(Schema.decode(TempWorkspaceTable))
27+
),
28+
29+
clean: ({ workspaceId }: { workspaceId: string }) =>
30+
query((_) =>
31+
_.temp_workspace.where("workspaceId").equals(workspaceId).delete()
32+
),
33+
};
34+
}),
35+
}
36+
) {}

apps/client/src/lib/services/workspace-manager.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Effect } from "effect";
1+
import { Effect, Schema } from "effect";
22
import { Dexie } from "../dexie";
3+
import { WorkspaceTable } from "../schema";
34

45
export class WorkspaceManager extends Effect.Service<WorkspaceManager>()(
56
"WorkspaceManager",
@@ -10,6 +11,19 @@ export class WorkspaceManager extends Effect.Service<WorkspaceManager>()(
1011
const { query } = yield* Dexie;
1112

1213
return {
14+
setToken: ({
15+
token,
16+
workspaceId,
17+
}: {
18+
workspaceId: typeof WorkspaceTable.Type.workspaceId;
19+
token: NonNullable<typeof WorkspaceTable.Type.token>;
20+
}) => query((_) => _.workspace.update(workspaceId, { token })),
21+
22+
put: (update: typeof WorkspaceTable.Type) =>
23+
Schema.encode(WorkspaceTable)(update).pipe(
24+
Effect.flatMap((data) => query((_) => _.workspace.put(data)))
25+
),
26+
1327
getAll: query((_) => _.workspace.toArray()),
1428

1529
getById: ({ workspaceId }: { workspaceId: string }) =>
@@ -21,7 +35,7 @@ export class WorkspaceManager extends Effect.Service<WorkspaceManager>()(
2135
.first()
2236
).pipe(Effect.flatMap(Effect.fromNullable)),
2337

24-
putCurrent: (workspaceId: string | undefined) =>
38+
createOrJoin: (workspaceId: string | undefined) =>
2539
query((_) =>
2640
_.workspace.toCollection().modify({ current: false })
2741
).pipe(

apps/client/src/routes/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ function HomeComponent() {
1313
const allWorkspaces = Route.useLoaderData();
1414
const navigate = useNavigate();
1515

16-
const [, putWorkspace] = useActionEffect((formData: FormData | undefined) =>
16+
const [, joinWorkspace] = useActionEffect((formData: FormData | undefined) =>
1717
Effect.gen(function* () {
1818
const workspaceId = formData?.get("workspaceId") as string | null;
19-
const workspace = yield* WorkspaceManager.putCurrent(
19+
const workspace = yield* WorkspaceManager.createOrJoin(
2020
workspaceId ?? undefined
2121
);
2222
yield* Effect.sync(() =>
@@ -41,13 +41,13 @@ function HomeComponent() {
4141
</Link>
4242
))}
4343

44-
<form action={putWorkspace}>
44+
<form action={joinWorkspace}>
4545
<input type="text" name="workspaceId" placeholder="Workspace id" />
4646
<button type="submit">Switch workspace</button>
4747
</form>
4848

4949
<div>
50-
<button type="button" onClick={() => putWorkspace(undefined)}>
50+
<button type="button" onClick={() => joinWorkspace(undefined)}>
5151
Create workspace
5252
</button>
5353
</div>

apps/client/src/workers/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Schema } from "effect";
22

33
export class Bootstrap extends Schema.TaggedRequest<Bootstrap>()("Bootstrap", {
4-
failure: Schema.Never,
4+
failure: Schema.String,
55
payload: { workspaceId: Schema.String },
66
success: Schema.Boolean,
77
}) {}

apps/client/src/workers/sync.ts

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,87 @@
11
import { WorkerRunner } from "@effect/platform";
22
import { BrowserWorkerRunner } from "@effect/platform-browser";
33
import { Effect, Layer } from "effect";
4+
import { ApiClient } from "../lib/api-client";
5+
import { Dexie } from "../lib/dexie";
6+
import { RuntimeClient } from "../lib/runtime-client";
7+
import { TempWorkspace } from "../lib/services/temp-workspace";
8+
import { WorkspaceManager } from "../lib/services/workspace-manager";
49
import { WorkerMessage } from "./schema";
510

611
const WorkerLive = WorkerRunner.layerSerialized(WorkerMessage, {
7-
Bootstrap: ({ workspaceId }) =>
12+
Bootstrap: (params) =>
813
Effect.gen(function* () {
9-
yield* Effect.log(`Bootstrapping workspace ${workspaceId}`);
14+
const { client } = yield* ApiClient;
15+
const { initClient } = yield* Dexie;
16+
17+
const manager = yield* WorkspaceManager;
18+
const temp = yield* TempWorkspace;
19+
20+
yield* Effect.log(`Running workspace '${params.workspaceId}'`);
21+
22+
const workspace = yield* manager
23+
.getById({ workspaceId: params.workspaceId })
24+
.pipe(Effect.mapError(() => "Get workspace error"));
25+
26+
const clientId = yield* initClient.pipe(
27+
Effect.mapError(() => "Init client error")
28+
);
29+
30+
const tempUpdates = yield* temp
31+
.get({ workspaceId: workspace.workspaceId })
32+
.pipe(Effect.mapError(() => "Get temp workspace error"));
33+
34+
if (tempUpdates !== null) {
35+
const response = yield* Effect.fromNullable(workspace.token).pipe(
36+
Effect.flatMap((token) =>
37+
client.syncData
38+
.push({
39+
headers: { Authorization: `Bearer ${token}` },
40+
path: { workspaceId: workspace.workspaceId },
41+
payload: { clientId, snapshot: tempUpdates.snapshot },
42+
})
43+
.pipe(
44+
Effect.map((response) => ({ ...response, token })),
45+
Effect.mapError(() => "Sync push error")
46+
)
47+
),
48+
Effect.orElse(() =>
49+
client.syncAuth
50+
.generateToken({
51+
payload: {
52+
clientId,
53+
snapshot: tempUpdates.snapshot,
54+
workspaceId: params.workspaceId,
55+
},
56+
})
57+
.pipe(
58+
Effect.tap(({ token }) =>
59+
manager.setToken({
60+
workspaceId: workspace.workspaceId,
61+
token,
62+
})
63+
),
64+
Effect.mapError(() => "Generate token error")
65+
)
66+
)
67+
);
68+
69+
yield* manager
70+
.put({
71+
workspaceId: response.workspaceId,
72+
snapshot: response.snapshot,
73+
token: response.token,
74+
version: new Uint8Array(), // TODO
75+
})
76+
.pipe(Effect.mapError(() => "Put workspace error"));
77+
78+
yield* Effect.log("Sync completed");
79+
} else {
80+
yield* Effect.log("No sync updates");
81+
}
82+
1083
return true;
1184
}),
1285
}).pipe(Layer.provide(BrowserWorkerRunner.layer));
1386

14-
Effect.runFork(WorkerRunner.launch(WorkerLive));
87+
RuntimeClient.runFork(WorkerRunner.launch(WorkerLive));

packages/sync/src/main.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ export class SyncAuthGroup extends HttpApiGroup.make("syncAuth")
5252
})
5353
)
5454
.addError(Schema.String)
55-
.addSuccess(Schema.Struct({ token: Schema.String }))
55+
.addSuccess(
56+
Schema.Struct({
57+
token: Schema.String,
58+
workspaceId: WorkspaceTable.fields.workspaceId,
59+
createdAt: WorkspaceTable.fields.createdAt,
60+
snapshot: WorkspaceTable.fields.snapshot,
61+
})
62+
)
5663
)
5764
.add(
5865
/**

0 commit comments

Comments
 (0)