Skip to content

Commit a04e86e

Browse files
committed
Update Result docs with block scope pattern
1 parent 97a39b7 commit a04e86e

7 files changed

Lines changed: 109 additions & 84 deletions

File tree

.changeset/silver-jars-smell.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
"@evolu/common": patch
3+
---
4+
5+
Update Result documentation with block scope pattern for multiple void operations
6+
7+
```ts
8+
// Before - inventing names to avoid name clash
9+
const baseTables = createBaseSqliteStorageTables(deps);
10+
if (!baseTables.ok) return baseTables;
11+
12+
const relayTables = createRelayStorageTables(deps);
13+
if (!relayTables.ok) return relayTables;
14+
15+
// After - block scopes avoid name clash
16+
{
17+
const result = createBaseSqliteStorageTables(deps);
18+
if (!result.ok) return result;
19+
}
20+
{
21+
const result = createRelayStorageTables(deps);
22+
if (!result.ok) return result;
23+
}
24+
```

.github/copilot-instructions.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,10 @@ const parseJson = (value: string): Result<unknown, ParseJsonError> =>
112112

113113
// ✅ Good - Sequential operations with short-circuiting
114114
const processData = (deps: DataDeps) => {
115-
const step1Result = doStep1(deps);
116-
if (!step1Result.ok) return step1Result;
115+
const foo = doFoo(deps);
116+
if (!foo.ok) return foo;
117117

118-
const step2Result = doStep2(deps)(step1Result.value);
119-
if (!step2Result.ok) return step2Result;
120-
121-
return ok(step2Result.value);
118+
return doStep2(deps)(foo.value);
122119
};
123120

124121
// ❌ Avoid - Implementation error in public API

packages/common/src/Result.ts

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Task } from "./Task.js";
2+
13
/**
24
* The problem with throwing an exception in JavaScript is that the caught error
35
* is always of an unknown type. The unknown type is a problem because we can't
@@ -82,32 +84,38 @@
8284
* };
8385
* ```
8486
*
87+
* For lazy, cancellable async operations, see {@link Task}.
88+
*
8589
* ### Naming convention
8690
*
87-
* - For values: `const user = getUser()`
88-
* - For a single void operation: `const result = foo()`
89-
* - For multiple void operations: use descriptive names for all
91+
* - **For values you need:** use a name without Result suffix (`user`, `config`)
92+
* - **For void operations:** use `result` (no value to name)
93+
*
94+
* For multiple void operations, use block scopes to avoid potentially long
95+
* names like `createBaseTablesResult`, `createRelayTablesResult`, or counters
96+
* like `result1`, `result2`:
9097
*
9198
* ```ts
9299
* const processUser = () => {
93-
* // we have a value
94100
* const user = getUser();
95101
* if (!user.ok) return user;
96102
*
97-
* // single void operation
98103
* const result = saveToDatabase(user.value);
99104
* if (!result.ok) return result;
100105
*
101106
* return ok();
102107
* };
103108
*
104109
* const setupDatabase = () => {
105-
* // multiple void operations - use descriptive names
106-
* const baseTables = createBaseTables();
107-
* if (!baseTables.ok) return baseTables;
108-
*
109-
* const relayTables = createRelayTables();
110-
* if (!relayTables.ok) return relayTables;
110+
* // Multiple void operations - use block scopes to avoid name clash
111+
* {
112+
* const result = createBaseTables();
113+
* if (!result.ok) return result;
114+
* }
115+
* {
116+
* const result = createRelayTables();
117+
* if (!result.ok) return result;
118+
* }
111119
*
112120
* return ok();
113121
* };
@@ -124,30 +132,31 @@
124132
* schema, and initializes the database, stopping on the first error:
125133
*
126134
* ```ts
127-
* const resetResult = deps.sqlite.transaction(() => {
128-
* const dropAllTablesResult = dropAllTables(deps);
129-
* if (!dropAllTablesResult.ok) return dropAllTablesResult;
135+
* const result = deps.sqlite.transaction(() => {
136+
* const result = dropAllTables(deps);
137+
* if (!result.ok) return result;
130138
*
131139
* if (message.restore) {
132140
* const dbSchema = getDbSchema(deps)();
133141
* if (!dbSchema.ok) return dbSchema;
134142
*
135-
* const ensureDbSchemaResult = ensureDbSchema(deps)(
136-
* message.restore.dbSchema,
137-
* dbSchema.value,
138-
* );
139-
* if (!ensureDbSchemaResult.ok) return ensureDbSchemaResult;
140-
*
141-
* const initializeDbResult = initializeDb(deps)(
142-
* message.restore.mnemonic,
143-
* );
144-
* if (!initializeDbResult.ok) return initializeDbResult;
143+
* {
144+
* const result = ensureDbSchema(deps)(
145+
* message.restore.dbSchema,
146+
* dbSchema.value,
147+
* );
148+
* if (!result.ok) return result;
149+
* }
150+
* {
151+
* const result = initializeDb(deps)(message.restore.mnemonic);
152+
* if (!result.ok) return result;
153+
* }
145154
* }
146155
* return ok();
147156
* });
148157
*
149-
* if (!resetResult.ok) {
150-
* deps.postMessage({ type: "onError", error: resetResult.error });
158+
* if (!result.ok) {
159+
* deps.postMessage({ type: "onError", error: result.error });
151160
* return;
152161
* }
153162
* ```
@@ -254,13 +263,13 @@
254263
* ```ts
255264
* // ✅ Safe to return void - unsafe code is wrapped and error is handled
256265
* const processData = (data: string): void => {
257-
* const parseResult = trySync(
266+
* const result = trySync(
258267
* () => JSON.parse(data),
259268
* (error) => ({ type: "ParseError", message: String(error) }),
260269
* );
261270
*
262-
* if (!parseResult.ok) {
263-
* logError(parseResult.error);
271+
* if (!result.ok) {
272+
* logError(result.error);
264273
* return;
265274
* }
266275
*

packages/common/src/Task.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -570,10 +570,12 @@ export const retry = <T, E>(
570570
}
571571

572572
// Wait before retry
573-
const delayResult = await wait(NonNegativeInt.orThrow(delay))(context);
574-
if (!delayResult.ok) {
575-
// If delay was aborted, return AbortError (will be handled by toTask)
576-
return delayResult;
573+
{
574+
const result = await wait(NonNegativeInt.orThrow(delay))(context);
575+
if (!result.ok) {
576+
// If delay was aborted, return AbortError (will be handled by toTask)
577+
return result;
578+
}
577579
}
578580
}
579581
});

packages/common/src/local-first/Protocol.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -972,12 +972,9 @@ export const applyProtocolMessageAsClient =
972972
const ownerIdBytes = ownerIdToOwnerIdBytes(ownerId);
973973

974974
if (isNonEmptyReadonlyArray(messages)) {
975-
const writeResult = await deps.storage.writeMessages(
976-
ownerIdBytes,
977-
messages,
978-
);
975+
const result = await deps.storage.writeMessages(ownerIdBytes, messages);
979976
// Errors are handled by the Storage. Here we just stop syncing.
980-
if (!writeResult.ok) return ok({ type: "no-response" });
977+
if (!result.ok) return ok({ type: "no-response" });
981978
}
982979

983980
// Now: No writeKey, no sync.
@@ -1006,10 +1003,10 @@ export const applyProtocolMessageAsClient =
10061003
rangesMaxSize: options.rangesMaxSize,
10071004
});
10081005

1009-
const syncResult = sync(deps)(ranges, output, ownerIdBytes);
1006+
const result = sync(deps)(ranges, output, ownerIdBytes);
10101007

10111008
// Client sync error (handled via Storage) or no changes.
1012-
if (!syncResult.ok || !syncResult.value) {
1009+
if (!result.ok || !result.value) {
10131010
return ok({ type: "no-response" });
10141011
}
10151012

@@ -1126,14 +1123,11 @@ export const applyProtocolMessageAsRelay =
11261123
});
11271124
}
11281125

1129-
const writeResult = await deps.storage.writeMessages(
1130-
ownerIdBytes,
1131-
messages,
1132-
);
1126+
const result = await deps.storage.writeMessages(ownerIdBytes, messages);
11331127

1134-
if (!writeResult.ok) {
1128+
if (!result.ok) {
11351129
const errorCode =
1136-
writeResult.error.type === "StorageWriteError"
1130+
result.error.type === "StorageWriteError"
11371131
? ProtocolErrorCode.WriteError
11381132
: ProtocolErrorCode.QuotaError;
11391133
const message = createProtocolMessageBuffer(ownerId, {
@@ -1189,13 +1183,13 @@ export const applyProtocolMessageAsRelay =
11891183
return ok({ type: "response", message: output.unwrap() });
11901184
}
11911185

1192-
const syncResult = sync(deps)(ranges, output, ownerIdBytes);
1186+
const result = sync(deps)(ranges, output, ownerIdBytes);
11931187

1194-
const message = syncResult.ok
1188+
const message = result.ok
11951189
? output.unwrap()
11961190
: createProtocolMessageBuffer(ownerId, {
11971191
messageType: MessageType.Response,
1198-
errorCode: syncResult.error,
1192+
errorCode: result.error,
11991193
}).unwrap();
12001194

12011195
// Non-initiators always respond to provide sync completion feedback,

packages/common/src/local-first/Relay.ts

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,8 @@ export const createRelaySqliteStorage =
217217
(storedBytes ?? 0) + incomingBytes,
218218
);
219219

220-
const withinQuotaResult = config.isOwnerWithinQuota(
221-
ownerId,
222-
newStoredBytes,
223-
);
224-
const isWithinQuota = isAsync(withinQuotaResult)
225-
? await withinQuotaResult
226-
: withinQuotaResult;
220+
const result = config.isOwnerWithinQuota(ownerId, newStoredBytes);
221+
const isWithinQuota = isAsync(result) ? await result : result;
227222
if (!isWithinQuota) {
228223
return err({ type: "StorageQuotaError", ownerId });
229224
}
@@ -240,30 +235,31 @@ export const createRelaySqliteStorage =
240235
lastTimestamp,
241236
);
242237

243-
const insertTimestampResult = sqliteStorageBase.insertTimestamp(
244-
ownerIdBytes,
245-
timestamp,
246-
strategy,
247-
);
248-
if (!insertTimestampResult.ok) return insertTimestampResult;
249-
250-
const insertMessage = deps.sqlite.exec(sql`
251-
insert into evolu_message ("ownerId", "timestamp", "change")
252-
values (${ownerIdBytes}, ${timestamp}, ${change})
253-
on conflict do nothing;
254-
`);
255-
if (!insertMessage.ok) return insertMessage;
238+
{
239+
const result = sqliteStorageBase.insertTimestamp(
240+
ownerIdBytes,
241+
timestamp,
242+
strategy,
243+
);
244+
if (!result.ok) return result;
245+
}
246+
247+
{
248+
const result = deps.sqlite.exec(sql`
249+
insert into evolu_message ("ownerId", "timestamp", "change")
250+
values (${ownerIdBytes}, ${timestamp}, ${change})
251+
on conflict do nothing;
252+
`);
253+
if (!result.ok) return result;
254+
}
256255
}
257256

258-
const updateUsage = updateOwnerUsage(deps)(
257+
return updateOwnerUsage(deps)(
259258
ownerIdBytes,
260259
newStoredBytes,
261260
firstTimestamp,
262261
lastTimestamp,
263262
);
264-
if (!updateUsage.ok) return updateUsage;
265-
266-
return ok();
267263
});
268264
})();
269265

packages/nodejs/src/local-first/Relay.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,14 @@ const createNodeJsRelayWithDeps =
8787
const depsWithSqlite = { ...deps, sqlite: sqlite.value };
8888

8989
if (!dbFileExists) {
90-
const baseTables = createBaseSqliteStorageTables(depsWithSqlite);
91-
if (!baseTables.ok) return baseTables;
92-
93-
const relayTables = createRelayStorageTables(depsWithSqlite);
94-
if (!relayTables.ok) return relayTables;
90+
{
91+
const result = createBaseSqliteStorageTables(depsWithSqlite);
92+
if (!result.ok) return result;
93+
}
94+
{
95+
const result = createRelayStorageTables(depsWithSqlite);
96+
if (!result.ok) return result;
97+
}
9598
}
9699

97100
const storage = createRelaySqliteStorage(depsWithSqlite)({

0 commit comments

Comments
 (0)