effect 4.0.0-beta.44 → 4.0.0-beta.46
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/dist/Equal.d.ts.map +1 -1
- package/dist/Equal.js +16 -0
- package/dist/Equal.js.map +1 -1
- package/dist/Hash.js +1 -1
- package/dist/Hash.js.map +1 -1
- package/dist/Semaphore.d.ts +1 -1
- package/dist/Semaphore.d.ts.map +1 -1
- package/dist/Semaphore.js +1 -3
- package/dist/Semaphore.js.map +1 -1
- package/dist/unstable/ai/McpServer.d.ts.map +1 -1
- package/dist/unstable/ai/McpServer.js +24 -21
- package/dist/unstable/ai/McpServer.js.map +1 -1
- package/dist/unstable/eventlog/Event.d.ts +0 -6
- package/dist/unstable/eventlog/Event.d.ts.map +1 -1
- package/dist/unstable/eventlog/Event.js +0 -5
- package/dist/unstable/eventlog/Event.js.map +1 -1
- package/dist/unstable/eventlog/EventGroup.d.ts +0 -2
- package/dist/unstable/eventlog/EventGroup.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventGroup.js +0 -2
- package/dist/unstable/eventlog/EventGroup.js.map +1 -1
- package/dist/unstable/eventlog/EventJournal.d.ts +22 -5
- package/dist/unstable/eventlog/EventJournal.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventJournal.js +126 -67
- package/dist/unstable/eventlog/EventJournal.js.map +1 -1
- package/dist/unstable/eventlog/EventLog.d.ts +88 -34
- package/dist/unstable/eventlog/EventLog.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLog.js +215 -141
- package/dist/unstable/eventlog/EventLog.js.map +1 -1
- package/dist/unstable/eventlog/EventLogEncryption.d.ts +9 -7
- package/dist/unstable/eventlog/EventLogEncryption.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogEncryption.js +13 -15
- package/dist/unstable/eventlog/EventLogEncryption.js.map +1 -1
- package/dist/unstable/eventlog/EventLogMessage.d.ts +228 -0
- package/dist/unstable/eventlog/EventLogMessage.d.ts.map +1 -0
- package/dist/unstable/eventlog/EventLogMessage.js +214 -0
- package/dist/unstable/eventlog/EventLogMessage.js.map +1 -0
- package/dist/unstable/eventlog/EventLogRemote.d.ts +109 -194
- package/dist/unstable/eventlog/EventLogRemote.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogRemote.js +165 -320
- package/dist/unstable/eventlog/EventLogRemote.js.map +1 -1
- package/dist/unstable/eventlog/EventLogServer.d.ts +25 -47
- package/dist/unstable/eventlog/EventLogServer.d.ts.map +1 -1
- package/dist/unstable/eventlog/EventLogServer.js +127 -198
- package/dist/unstable/eventlog/EventLogServer.js.map +1 -1
- package/dist/unstable/eventlog/EventLogServerEncrypted.d.ts +60 -0
- package/dist/unstable/eventlog/EventLogServerEncrypted.d.ts.map +1 -0
- package/dist/unstable/eventlog/EventLogServerEncrypted.js +166 -0
- package/dist/unstable/eventlog/EventLogServerEncrypted.js.map +1 -0
- package/dist/unstable/eventlog/EventLogServerUnencrypted.d.ts +183 -0
- package/dist/unstable/eventlog/EventLogServerUnencrypted.d.ts.map +1 -0
- package/dist/unstable/eventlog/EventLogServerUnencrypted.js +461 -0
- package/dist/unstable/eventlog/EventLogServerUnencrypted.js.map +1 -0
- package/dist/unstable/eventlog/EventLogSessionAuth.d.ts +117 -0
- package/dist/unstable/eventlog/EventLogSessionAuth.d.ts.map +1 -0
- package/dist/unstable/eventlog/EventLogSessionAuth.js +284 -0
- package/dist/unstable/eventlog/EventLogSessionAuth.js.map +1 -0
- package/dist/unstable/eventlog/{SqlEventLogJournal.d.ts → SqlEventJournal.d.ts} +2 -2
- package/dist/unstable/eventlog/SqlEventJournal.d.ts.map +1 -0
- package/dist/unstable/eventlog/{SqlEventLogJournal.js → SqlEventJournal.js} +20 -14
- package/dist/unstable/eventlog/SqlEventJournal.js.map +1 -0
- package/dist/unstable/eventlog/{SqlEventLogServer.d.ts → SqlEventLogServerEncrypted.d.ts} +5 -5
- package/dist/unstable/eventlog/SqlEventLogServerEncrypted.d.ts.map +1 -0
- package/dist/unstable/eventlog/{SqlEventLogServer.js → SqlEventLogServerEncrypted.js} +65 -24
- package/dist/unstable/eventlog/SqlEventLogServerEncrypted.js.map +1 -0
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts +25 -0
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.d.ts.map +1 -0
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js +354 -0
- package/dist/unstable/eventlog/SqlEventLogServerUnencrypted.js.map +1 -0
- package/dist/unstable/eventlog/index.d.ts +22 -2
- package/dist/unstable/eventlog/index.d.ts.map +1 -1
- package/dist/unstable/eventlog/index.js +22 -2
- package/dist/unstable/eventlog/index.js.map +1 -1
- package/dist/unstable/eventlog/internal/identityRootSecretDerivation.d.ts +2 -0
- package/dist/unstable/eventlog/internal/identityRootSecretDerivation.d.ts.map +1 -0
- package/dist/unstable/eventlog/internal/identityRootSecretDerivation.js +89 -0
- package/dist/unstable/eventlog/internal/identityRootSecretDerivation.js.map +1 -0
- package/dist/unstable/reactivity/AtomHttpApi.d.ts +1 -2
- package/dist/unstable/reactivity/AtomHttpApi.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomHttpApi.js +2 -2
- package/dist/unstable/reactivity/AtomHttpApi.js.map +1 -1
- package/dist/unstable/reactivity/AtomRpc.d.ts +1 -2
- package/dist/unstable/reactivity/AtomRpc.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomRpc.js +3 -3
- package/dist/unstable/reactivity/AtomRpc.js.map +1 -1
- package/dist/unstable/rpc/Rpc.d.ts +25 -4
- package/dist/unstable/rpc/Rpc.d.ts.map +1 -1
- package/dist/unstable/rpc/Rpc.js +26 -0
- package/dist/unstable/rpc/Rpc.js.map +1 -1
- package/dist/unstable/rpc/RpcClient.d.ts +3 -13
- package/dist/unstable/rpc/RpcClient.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcClient.js +47 -23
- package/dist/unstable/rpc/RpcClient.js.map +1 -1
- package/dist/unstable/rpc/RpcGroup.d.ts +1 -1
- package/dist/unstable/rpc/RpcGroup.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcMiddleware.d.ts +2 -2
- package/dist/unstable/rpc/RpcMiddleware.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcServer.d.ts.map +1 -1
- package/dist/unstable/rpc/RpcServer.js +3 -2
- package/dist/unstable/rpc/RpcServer.js.map +1 -1
- package/dist/unstable/rpc/Utils.d.ts +6 -0
- package/dist/unstable/rpc/Utils.d.ts.map +1 -1
- package/dist/unstable/rpc/Utils.js +44 -0
- package/dist/unstable/rpc/Utils.js.map +1 -1
- package/dist/unstable/schema/Model.d.ts +2 -2
- package/dist/unstable/schema/Model.d.ts.map +1 -1
- package/dist/unstable/schema/Model.js +2 -4
- package/dist/unstable/schema/Model.js.map +1 -1
- package/dist/unstable/schema/VariantSchema.d.ts +1 -1
- package/dist/unstable/schema/VariantSchema.d.ts.map +1 -1
- package/dist/unstable/schema/VariantSchema.js +1 -12
- package/dist/unstable/schema/VariantSchema.js.map +1 -1
- package/dist/unstable/workers/Transferable.d.ts +1 -1
- package/dist/unstable/workers/Transferable.d.ts.map +1 -1
- package/dist/unstable/workers/Transferable.js +1 -1
- package/dist/unstable/workers/Transferable.js.map +1 -1
- package/package.json +1 -1
- package/src/Equal.ts +17 -0
- package/src/Hash.ts +2 -2
- package/src/Semaphore.ts +2 -4
- package/src/unstable/ai/McpServer.ts +24 -22
- package/src/unstable/eventlog/Event.ts +0 -8
- package/src/unstable/eventlog/EventGroup.ts +0 -4
- package/src/unstable/eventlog/EventJournal.ts +144 -76
- package/src/unstable/eventlog/EventLog.ts +342 -221
- package/src/unstable/eventlog/EventLogEncryption.ts +16 -30
- package/src/unstable/eventlog/EventLogMessage.ts +277 -0
- package/src/unstable/eventlog/EventLogRemote.ts +261 -408
- package/src/unstable/eventlog/EventLogServer.ts +182 -274
- package/src/unstable/eventlog/EventLogServerEncrypted.ts +206 -0
- package/src/unstable/eventlog/EventLogServerUnencrypted.ts +749 -0
- package/src/unstable/eventlog/EventLogSessionAuth.ts +437 -0
- package/src/unstable/eventlog/{SqlEventLogJournal.ts → SqlEventJournal.ts} +26 -18
- package/src/unstable/eventlog/{SqlEventLogServer.ts → SqlEventLogServerEncrypted.ts} +102 -40
- package/src/unstable/eventlog/SqlEventLogServerUnencrypted.ts +500 -0
- package/src/unstable/eventlog/index.ts +27 -2
- package/src/unstable/eventlog/internal/identityRootSecretDerivation.ts +153 -0
- package/src/unstable/reactivity/AtomHttpApi.ts +23 -8
- package/src/unstable/reactivity/AtomRpc.ts +16 -5
- package/src/unstable/rpc/Rpc.ts +42 -4
- package/src/unstable/rpc/RpcClient.ts +59 -24
- package/src/unstable/rpc/RpcGroup.ts +1 -1
- package/src/unstable/rpc/RpcMiddleware.ts +2 -2
- package/src/unstable/rpc/RpcServer.ts +5 -3
- package/src/unstable/rpc/Utils.ts +59 -0
- package/src/unstable/schema/Model.ts +4 -6
- package/src/unstable/schema/VariantSchema.ts +4 -17
- package/src/unstable/workers/Transferable.ts +9 -11
- package/dist/unstable/eventlog/SqlEventLogJournal.d.ts.map +0 -1
- package/dist/unstable/eventlog/SqlEventLogJournal.js.map +0 -1
- package/dist/unstable/eventlog/SqlEventLogServer.d.ts.map +0 -1
- package/dist/unstable/eventlog/SqlEventLogServer.js.map +0 -1
package/src/Equal.ts
CHANGED
|
@@ -317,6 +317,11 @@ function compareObjects(self: object, that: object): boolean {
|
|
|
317
317
|
return false
|
|
318
318
|
}
|
|
319
319
|
return compareArrays(self, that)
|
|
320
|
+
} else if (ArrayBuffer.isView(self)) {
|
|
321
|
+
if (!ArrayBuffer.isView(that) || self.byteLength !== that.byteLength) {
|
|
322
|
+
return false
|
|
323
|
+
}
|
|
324
|
+
return compareTypedArrays(self as Uint8Array, that as Uint8Array)
|
|
320
325
|
} else if (self instanceof Map) {
|
|
321
326
|
if (!(that instanceof Map) || self.size !== that.size) {
|
|
322
327
|
return false
|
|
@@ -370,6 +375,18 @@ function compareArrays(self: Array<unknown>, that: Array<unknown>): boolean {
|
|
|
370
375
|
return true
|
|
371
376
|
}
|
|
372
377
|
|
|
378
|
+
function compareTypedArrays(self: Uint8Array, that: Uint8Array): boolean {
|
|
379
|
+
if (self.length !== that.length) {
|
|
380
|
+
return false
|
|
381
|
+
}
|
|
382
|
+
for (let i = 0; i < self.length; i++) {
|
|
383
|
+
if (self[i] !== that[i]) {
|
|
384
|
+
return false
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return true
|
|
388
|
+
}
|
|
389
|
+
|
|
373
390
|
function compareRecords(
|
|
374
391
|
self: Record<PropertyKey, unknown>,
|
|
375
392
|
that: Record<PropertyKey, unknown>
|
package/src/Hash.ts
CHANGED
|
@@ -116,8 +116,8 @@ export const hash: <A>(self: A) => number = <A>(self: A) => {
|
|
|
116
116
|
return self[symbol]()
|
|
117
117
|
} else if (typeof self === "function") {
|
|
118
118
|
return random(self)
|
|
119
|
-
} else if (Array.isArray(self)) {
|
|
120
|
-
return array(self)
|
|
119
|
+
} else if (Array.isArray(self) || ArrayBuffer.isView(self)) {
|
|
120
|
+
return array(self as any)
|
|
121
121
|
} else if (self instanceof Map) {
|
|
122
122
|
return hashMap(self)
|
|
123
123
|
} else if (self instanceof Set) {
|
package/src/Semaphore.ts
CHANGED
|
@@ -55,7 +55,7 @@ export interface Semaphore {
|
|
|
55
55
|
* If insufficient permits are available, the function will wait until they
|
|
56
56
|
* are released by other tasks.
|
|
57
57
|
*/
|
|
58
|
-
withPermit<A, E, R>(
|
|
58
|
+
withPermit<A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Runs an effect only if the specified number of permits are immediately
|
|
@@ -220,9 +220,7 @@ class SemaphoreImpl implements Semaphore {
|
|
|
220
220
|
)
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
return this.withPermits(1)
|
|
225
|
-
}
|
|
223
|
+
readonly withPermit = this.withPermits(1)
|
|
226
224
|
|
|
227
225
|
withPermitsIfAvailable(n: number) {
|
|
228
226
|
return <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
@@ -24,7 +24,7 @@ import { appendPreResponseHandlerUnsafe } from "../http/HttpEffect.ts"
|
|
|
24
24
|
import * as HttpRouter from "../http/HttpRouter.ts"
|
|
25
25
|
import * as HttpServerRequest from "../http/HttpServerRequest.ts"
|
|
26
26
|
import * as HttpServerResponse from "../http/HttpServerResponse.ts"
|
|
27
|
-
import
|
|
27
|
+
import * as Rpc from "../rpc/Rpc.ts"
|
|
28
28
|
import * as RpcClient from "../rpc/RpcClient.ts"
|
|
29
29
|
import type * as RpcGroup from "../rpc/RpcGroup.ts"
|
|
30
30
|
import * as RpcMessage from "../rpc/RpcMessage.ts"
|
|
@@ -353,9 +353,11 @@ export const run: (options: {
|
|
|
353
353
|
Effect.provideServiceEffect(
|
|
354
354
|
RpcClient.Protocol,
|
|
355
355
|
RpcClient.Protocol.make(Effect.fnUntraced(function*(writeResponse) {
|
|
356
|
-
|
|
356
|
+
let cid = 0
|
|
357
|
+
write = (message) => writeResponse(cid, message)
|
|
357
358
|
return {
|
|
358
|
-
send(request, _transferables) {
|
|
359
|
+
send(id, request, _transferables) {
|
|
360
|
+
cid = id
|
|
359
361
|
return protocol.send(clientId, {
|
|
360
362
|
...request,
|
|
361
363
|
headers: undefined,
|
|
@@ -377,8 +379,8 @@ export const run: (options: {
|
|
|
377
379
|
idleTimeToLive: 10000
|
|
378
380
|
})
|
|
379
381
|
|
|
380
|
-
const clientMiddleware = McpServerClientMiddleware.of((effect, {
|
|
381
|
-
const initializePayload = getInitializedClient(clientSessions,
|
|
382
|
+
const clientMiddleware = McpServerClientMiddleware.of((effect, { client, headers, rpc }) => {
|
|
383
|
+
const initializePayload = getInitializedClient(clientSessions, client.id, headers)
|
|
382
384
|
const isInitialize = rpc._tag === "initialize"
|
|
383
385
|
if (!isInitialize && !initializePayload) {
|
|
384
386
|
return Effect.die(new Error(`Mcp-Session-Id does not exist`))
|
|
@@ -387,9 +389,9 @@ export const run: (options: {
|
|
|
387
389
|
effect,
|
|
388
390
|
McpServerClient,
|
|
389
391
|
McpServerClient.of({
|
|
390
|
-
clientId,
|
|
392
|
+
clientId: client.id,
|
|
391
393
|
initializePayload: initializePayload!,
|
|
392
|
-
getClient: RcMap.get(clients,
|
|
394
|
+
getClient: RcMap.get(clients, client.id).pipe(
|
|
393
395
|
Effect.map(({ client }) => client)
|
|
394
396
|
)
|
|
395
397
|
})
|
|
@@ -429,7 +431,7 @@ export const run: (options: {
|
|
|
429
431
|
? handler.handler(request.payload, {
|
|
430
432
|
rpc,
|
|
431
433
|
requestId: RpcMessage.RequestId(request.id),
|
|
432
|
-
clientId,
|
|
434
|
+
client: new Rpc.ServerClient(clientId),
|
|
433
435
|
headers: Headers.fromInput(request.headers)
|
|
434
436
|
}) as Effect.Effect<void>
|
|
435
437
|
: Effect.void
|
|
@@ -1176,7 +1178,7 @@ const layerHandlers = (serverInfo: {
|
|
|
1176
1178
|
return ClientRpcs.of({
|
|
1177
1179
|
// Requests
|
|
1178
1180
|
ping: () => Effect.succeed({}),
|
|
1179
|
-
initialize(params, {
|
|
1181
|
+
initialize(params, { client }) {
|
|
1180
1182
|
const requestedVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(params.protocolVersion)
|
|
1181
1183
|
? params.protocolVersion
|
|
1182
1184
|
: LATEST_PROTOCOL_VERSION
|
|
@@ -1215,7 +1217,7 @@ const layerHandlers = (serverInfo: {
|
|
|
1215
1217
|
[mcpProtocolVersionHeader]: requestedVersion
|
|
1216
1218
|
})))
|
|
1217
1219
|
} else {
|
|
1218
|
-
options.clientSessions.set(String(
|
|
1220
|
+
options.clientSessions.set(String(client.id), params)
|
|
1219
1221
|
}
|
|
1220
1222
|
return Effect.succeed({
|
|
1221
1223
|
capabilities,
|
|
@@ -1255,15 +1257,15 @@ const layerHandlers = (serverInfo: {
|
|
|
1255
1257
|
server.getPromptResult(r).pipe(
|
|
1256
1258
|
Effect.provideService(CurrentLogLevel, currentLogLevel)
|
|
1257
1259
|
),
|
|
1258
|
-
"prompts/list": (_, {
|
|
1260
|
+
"prompts/list": (_, { client, headers }) =>
|
|
1259
1261
|
Effect.sync(() => {
|
|
1260
|
-
const
|
|
1261
|
-
return new ListPromptsResult({ prompts: filterByClient(
|
|
1262
|
+
const initialized = getInitializedClient(options.clientSessions, client.id, headers)
|
|
1263
|
+
return new ListPromptsResult({ prompts: filterByClient(initialized, server.prompts, "prompt") })
|
|
1262
1264
|
}),
|
|
1263
|
-
"resources/list": (_, {
|
|
1265
|
+
"resources/list": (_, { client, headers }) =>
|
|
1264
1266
|
Effect.sync(() => {
|
|
1265
|
-
const
|
|
1266
|
-
return new ListResourcesResult({ resources: filterByClient(
|
|
1267
|
+
const initialized = getInitializedClient(options.clientSessions, client.id, headers)
|
|
1268
|
+
return new ListResourcesResult({ resources: filterByClient(initialized, server.resources, "resource") })
|
|
1267
1269
|
}),
|
|
1268
1270
|
"resources/read": ({ uri }) =>
|
|
1269
1271
|
server.findResource(uri).pipe(
|
|
@@ -1273,22 +1275,22 @@ const layerHandlers = (serverInfo: {
|
|
|
1273
1275
|
InternalError.notImplemented.asEffect(),
|
|
1274
1276
|
"resources/unsubscribe": () =>
|
|
1275
1277
|
InternalError.notImplemented.asEffect(),
|
|
1276
|
-
"resources/templates/list": (_, {
|
|
1278
|
+
"resources/templates/list": (_, { client, headers }) =>
|
|
1277
1279
|
Effect.sync(() => {
|
|
1278
|
-
const
|
|
1280
|
+
const initialized = getInitializedClient(options.clientSessions, client.id, headers)
|
|
1279
1281
|
return new ListResourceTemplatesResult({
|
|
1280
|
-
resourceTemplates: filterByClient(
|
|
1282
|
+
resourceTemplates: filterByClient(initialized, server.resourceTemplates, "template")
|
|
1281
1283
|
})
|
|
1282
1284
|
}),
|
|
1283
1285
|
"tools/call": (r) =>
|
|
1284
1286
|
server.callTool(r).pipe(
|
|
1285
1287
|
Effect.provideService(CurrentLogLevel, currentLogLevel)
|
|
1286
1288
|
),
|
|
1287
|
-
"tools/list": (_, {
|
|
1289
|
+
"tools/list": (_, { client, headers }) =>
|
|
1288
1290
|
Effect.sync(() => {
|
|
1289
|
-
const
|
|
1291
|
+
const initialized = getInitializedClient(options.clientSessions, client.id, headers)
|
|
1290
1292
|
return new ListToolsResult({
|
|
1291
|
-
tools: filterByClient(
|
|
1293
|
+
tools: filterByClient(initialized, server.tools, "tool")
|
|
1292
1294
|
})
|
|
1293
1295
|
}),
|
|
1294
1296
|
|
|
@@ -38,7 +38,6 @@ export interface Event<
|
|
|
38
38
|
> {
|
|
39
39
|
readonly [TypeId]: TypeId
|
|
40
40
|
readonly tag: Tag
|
|
41
|
-
readonly key: string
|
|
42
41
|
readonly primaryKey: (payload: Schema.Schema.Type<Payload>) => string
|
|
43
42
|
readonly payload: Payload
|
|
44
43
|
readonly payloadMsgPack: Msgpack.schema<Payload>
|
|
@@ -62,7 +61,6 @@ export interface EventHandler<in out Tag extends string> {
|
|
|
62
61
|
export interface Any {
|
|
63
62
|
readonly [TypeId]: TypeId
|
|
64
63
|
readonly tag: string
|
|
65
|
-
readonly key: string
|
|
66
64
|
readonly primaryKey: (payload: any) => string
|
|
67
65
|
readonly payload: Schema.Top
|
|
68
66
|
readonly payloadMsgPack: Msgpack.schema<Schema.Top>
|
|
@@ -312,7 +310,6 @@ export function make(options: {
|
|
|
312
310
|
const error = options.error ?? Schema.Never
|
|
313
311
|
return Object.assign(Object.create(Proto), {
|
|
314
312
|
tag: options.tag,
|
|
315
|
-
key: serviceKey(options.tag),
|
|
316
313
|
primaryKey: options.primaryKey,
|
|
317
314
|
payload,
|
|
318
315
|
payloadMsgPack: Msgpack.schema(payload),
|
|
@@ -338,8 +335,3 @@ export function addError(event: Any, error: Schema.Top): Any {
|
|
|
338
335
|
error: Schema.Union([event.error, error])
|
|
339
336
|
})
|
|
340
337
|
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* @since 4.0.0
|
|
344
|
-
*/
|
|
345
|
-
export const serviceKey = (tag: string): string => `effect/eventlog/Event/${tag}`
|
|
@@ -37,8 +37,6 @@ export const isEventGroup = (u: unknown): u is Any => Predicate.hasProperty(u, T
|
|
|
37
37
|
export interface EventGroup<
|
|
38
38
|
out Events extends Event.Any = Event.Any
|
|
39
39
|
> extends Pipeable {
|
|
40
|
-
readonly _?: symbol
|
|
41
|
-
readonly __type?: Events | undefined
|
|
42
40
|
readonly [TypeId]: TypeId
|
|
43
41
|
readonly events: Record.ReadonlyRecord<string, Events>
|
|
44
42
|
|
|
@@ -112,8 +110,6 @@ const makeProto = <
|
|
|
112
110
|
const EventGroupClass = (_: never) => {}
|
|
113
111
|
const group = Object.assign(EventGroupClass, {
|
|
114
112
|
[TypeId]: TypeId,
|
|
115
|
-
_: Symbol.for("~effect/eventlog/EventGroup"),
|
|
116
|
-
__type: undefined,
|
|
117
113
|
events: options.events,
|
|
118
114
|
add<
|
|
119
115
|
Tag extends string,
|
|
@@ -8,10 +8,13 @@ import * as Data from "../../Data.ts"
|
|
|
8
8
|
import * as DateTime from "../../DateTime.ts"
|
|
9
9
|
import * as Effect from "../../Effect.ts"
|
|
10
10
|
import * as Layer from "../../Layer.ts"
|
|
11
|
+
import * as Order from "../../Order.ts"
|
|
11
12
|
import * as PubSub from "../../PubSub.ts"
|
|
12
13
|
import * as Schema from "../../Schema.ts"
|
|
13
14
|
import type { Scope } from "../../Scope.ts"
|
|
15
|
+
import * as Semaphore from "../../Semaphore.ts"
|
|
14
16
|
import * as Msgpack from "../encoding/Msgpack.ts"
|
|
17
|
+
import type { StoreId } from "./EventLogMessage.ts"
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
20
|
* @since 4.0.0
|
|
@@ -44,17 +47,16 @@ export class EventJournal extends Context.Service<EventJournal, {
|
|
|
44
47
|
readonly remoteId: RemoteId
|
|
45
48
|
readonly entries: ReadonlyArray<RemoteEntry>
|
|
46
49
|
readonly compact?:
|
|
47
|
-
| ((uncommitted: ReadonlyArray<RemoteEntry>) => Effect.Effect<
|
|
48
|
-
ReadonlyArray<[compacted: ReadonlyArray<Entry>, remoteEntries: ReadonlyArray<RemoteEntry>]>,
|
|
49
|
-
EventJournalError
|
|
50
|
-
>)
|
|
50
|
+
| ((uncommitted: ReadonlyArray<RemoteEntry>) => Effect.Effect<ReadonlyArray<Entry>, EventJournalError>)
|
|
51
51
|
| undefined
|
|
52
52
|
readonly effect: (options: {
|
|
53
53
|
readonly entry: Entry
|
|
54
54
|
readonly conflicts: ReadonlyArray<Entry>
|
|
55
55
|
}) => Effect.Effect<void, EventJournalError>
|
|
56
56
|
}
|
|
57
|
-
) => Effect.Effect<
|
|
57
|
+
) => Effect.Effect<{
|
|
58
|
+
readonly duplicateEntries: ReadonlyArray<Entry>
|
|
59
|
+
}, EventJournalError>
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
62
|
* Return the uncommitted entries for a remote source.
|
|
@@ -78,6 +80,11 @@ export class EventJournal extends Context.Service<EventJournal, {
|
|
|
78
80
|
* Remove all data
|
|
79
81
|
*/
|
|
80
82
|
readonly destroy: Effect.Effect<void, EventJournalError>
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Run an effect with a lock on the journal.
|
|
86
|
+
*/
|
|
87
|
+
readonly withLock: (storeId: StoreId) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
|
|
81
88
|
}>()("effect/eventlog/EventJournal") {}
|
|
82
89
|
|
|
83
90
|
const TypeId = "effect/eventlog/EventJournal/EventJournalError" as const
|
|
@@ -142,13 +149,28 @@ export type EntryIdTypeId = "effect/eventlog/EventJournal/EntryId"
|
|
|
142
149
|
* @since 4.0.0
|
|
143
150
|
* @category entry
|
|
144
151
|
*/
|
|
145
|
-
export type EntryId = Uint8Array & Brand<EntryIdTypeId>
|
|
152
|
+
export type EntryId = Uint8Array<ArrayBuffer> & Brand<EntryIdTypeId>
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @since 4.0.0
|
|
156
|
+
* @category entry
|
|
157
|
+
*/
|
|
158
|
+
export const EntryId = (Schema.Uint8Array as Schema.instanceOf<Uint8Array<ArrayBuffer>>).pipe(
|
|
159
|
+
Schema.brand(EntryIdTypeId)
|
|
160
|
+
)
|
|
146
161
|
|
|
147
162
|
/**
|
|
148
163
|
* @since 4.0.0
|
|
149
164
|
* @category entry
|
|
150
165
|
*/
|
|
151
|
-
export const
|
|
166
|
+
export const EntryIdOrder = Order.make<EntryId>((a, b) => {
|
|
167
|
+
for (let i = 0; i < 16; i++) {
|
|
168
|
+
if (a[i] !== b[i]) {
|
|
169
|
+
return (a[i] - b[i]) < 0 ? -1 : 1
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return 0
|
|
173
|
+
})
|
|
152
174
|
|
|
153
175
|
/**
|
|
154
176
|
* @since 4.0.0
|
|
@@ -192,6 +214,11 @@ export class Entry extends Schema.Class<Entry>("effect/eventlog/EventJournal/Ent
|
|
|
192
214
|
*/
|
|
193
215
|
static decodeArray = Schema.decodeUnknownEffect(Entry.arrayMsgpack)
|
|
194
216
|
|
|
217
|
+
/**
|
|
218
|
+
* @since 4.0.0
|
|
219
|
+
*/
|
|
220
|
+
static Order = Order.make<Entry>((a, b) => EntryIdOrder(a.id, b.id))
|
|
221
|
+
|
|
195
222
|
/**
|
|
196
223
|
* @since 4.0.0
|
|
197
224
|
*/
|
|
@@ -232,6 +259,15 @@ export const makeMemory: Effect.Effect<EventJournal["Service"]> = Effect.gen(fun
|
|
|
232
259
|
const byId = new Map<string, Entry>()
|
|
233
260
|
const remotes = new Map<string, { sequence: number; missing: Array<Entry> }>()
|
|
234
261
|
const pubsub = yield* PubSub.unbounded<Entry>()
|
|
262
|
+
const storeSemaphores = new Map<StoreId, Semaphore.Semaphore>()
|
|
263
|
+
const withLock = (storeId: StoreId) => {
|
|
264
|
+
let semaphore = storeSemaphores.get(storeId)
|
|
265
|
+
if (!semaphore) {
|
|
266
|
+
semaphore = Semaphore.makeUnsafe(1)
|
|
267
|
+
storeSemaphores.set(storeId, semaphore)
|
|
268
|
+
}
|
|
269
|
+
return semaphore.withPermit
|
|
270
|
+
}
|
|
235
271
|
|
|
236
272
|
const ensureRemote = (remoteId: RemoteId) => {
|
|
237
273
|
const remoteIdString = Uuid.stringify(remoteId)
|
|
@@ -271,8 +307,10 @@ export const makeMemory: Effect.Effect<EventJournal["Service"]> = Effect.gen(fun
|
|
|
271
307
|
const remote = ensureRemote(options.remoteId)
|
|
272
308
|
const uncommittedRemotes: Array<RemoteEntry> = []
|
|
273
309
|
const uncommitted: Array<Entry> = []
|
|
310
|
+
const duplicateEntries: Array<Entry> = []
|
|
274
311
|
for (const remoteEntry of options.entries) {
|
|
275
312
|
if (byId.has(remoteEntry.entry.idString)) {
|
|
313
|
+
duplicateEntries.push(remoteEntry.entry)
|
|
276
314
|
if (remoteEntry.remoteSequence > remote.sequence) {
|
|
277
315
|
remote.sequence = remoteEntry.remoteSequence
|
|
278
316
|
}
|
|
@@ -282,36 +320,38 @@ export const makeMemory: Effect.Effect<EventJournal["Service"]> = Effect.gen(fun
|
|
|
282
320
|
uncommitted.push(remoteEntry.entry)
|
|
283
321
|
}
|
|
284
322
|
|
|
285
|
-
const
|
|
323
|
+
const compacted = options.compact
|
|
286
324
|
? yield* options.compact(uncommittedRemotes)
|
|
287
|
-
:
|
|
288
|
-
|
|
289
|
-
for (const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
conflicts.push(scannedEntry)
|
|
302
|
-
}
|
|
325
|
+
: uncommitted
|
|
326
|
+
|
|
327
|
+
for (const originEntry of compacted) {
|
|
328
|
+
const entryMillis = entryIdMillis(originEntry.id)
|
|
329
|
+
const conflicts: Array<Entry> = []
|
|
330
|
+
for (let i = journal.length - 1; i >= -1; i--) {
|
|
331
|
+
const entry = journal[i]
|
|
332
|
+
if (entry !== undefined && entry.createdAtMillis > entryMillis) {
|
|
333
|
+
continue
|
|
334
|
+
}
|
|
335
|
+
for (let j = i + 2; j < journal.length; j++) {
|
|
336
|
+
const scannedEntry = journal[j]!
|
|
337
|
+
if (scannedEntry.event === originEntry.event && scannedEntry.primaryKey === originEntry.primaryKey) {
|
|
338
|
+
conflicts.push(scannedEntry)
|
|
303
339
|
}
|
|
304
|
-
yield* options.effect({ entry: originEntry, conflicts })
|
|
305
|
-
break
|
|
306
340
|
}
|
|
341
|
+
yield* options.effect({ entry: originEntry, conflicts })
|
|
342
|
+
break
|
|
307
343
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
344
|
+
}
|
|
345
|
+
for (const remoteEntry of uncommittedRemotes) {
|
|
346
|
+
journal.push(remoteEntry.entry)
|
|
347
|
+
byId.set(remoteEntry.entry.idString, remoteEntry.entry)
|
|
348
|
+
if (remoteEntry.remoteSequence > remote.sequence) {
|
|
349
|
+
remote.sequence = remoteEntry.remoteSequence
|
|
313
350
|
}
|
|
314
|
-
|
|
351
|
+
}
|
|
352
|
+
journal.sort((a, b) => a.createdAtMillis - b.createdAtMillis)
|
|
353
|
+
return {
|
|
354
|
+
duplicateEntries
|
|
315
355
|
}
|
|
316
356
|
}),
|
|
317
357
|
withRemoteUncommited: (remoteId, f) =>
|
|
@@ -338,7 +378,8 @@ export const makeMemory: Effect.Effect<EventJournal["Service"]> = Effect.gen(fun
|
|
|
338
378
|
journal.length = 0
|
|
339
379
|
byId.clear()
|
|
340
380
|
remotes.clear()
|
|
341
|
-
})
|
|
381
|
+
}),
|
|
382
|
+
withLock
|
|
342
383
|
})
|
|
343
384
|
})
|
|
344
385
|
|
|
@@ -413,6 +454,7 @@ export const makeIndexedDb = (options?: {
|
|
|
413
454
|
writeFromRemote: Effect.fnUntraced(function*(options) {
|
|
414
455
|
const uncommitted: Array<Entry> = []
|
|
415
456
|
const uncommittedRemotes: Array<RemoteEntry> = []
|
|
457
|
+
const duplicateEntries: Array<Entry> = []
|
|
416
458
|
|
|
417
459
|
yield* Effect.callback<void, EventJournalError>((resume) => {
|
|
418
460
|
const tx = db.transaction(["entries", "remotes"], "readwrite")
|
|
@@ -426,6 +468,7 @@ export const makeIndexedDb = (options?: {
|
|
|
426
468
|
const entryIdKey = entry.id as IDBValidKey
|
|
427
469
|
entries.get(entryIdKey).onsuccess = (event) => {
|
|
428
470
|
if (event.target && "result" in event.target && event.target.result) {
|
|
471
|
+
duplicateEntries.push(entry)
|
|
429
472
|
remotes.put({
|
|
430
473
|
remoteId: options.remoteId,
|
|
431
474
|
entryId: entry.id,
|
|
@@ -445,58 +488,58 @@ export const makeIndexedDb = (options?: {
|
|
|
445
488
|
return Effect.sync(() => tx.abort())
|
|
446
489
|
})
|
|
447
490
|
|
|
448
|
-
const
|
|
491
|
+
const compacted = options.compact
|
|
449
492
|
? yield* options.compact(uncommittedRemotes)
|
|
450
|
-
:
|
|
451
|
-
|
|
452
|
-
for (const [compacted, remoteEntries] of brackets) {
|
|
453
|
-
for (const originEntry of compacted) {
|
|
454
|
-
const conflicts: Array<Entry> = []
|
|
455
|
-
yield* Effect.callback<void, EventJournalError>((resume) => {
|
|
456
|
-
const tx = db.transaction("entries", "readonly")
|
|
457
|
-
const entries = tx.objectStore("entries")
|
|
458
|
-
const cursorRequest = entries.index("id").openCursor(
|
|
459
|
-
IDBKeyRange.lowerBound(originEntry.id as IDBValidKey, true),
|
|
460
|
-
"next"
|
|
461
|
-
)
|
|
462
|
-
cursorRequest.onsuccess = () => {
|
|
463
|
-
const cursor = cursorRequest.result
|
|
464
|
-
if (!cursor) return
|
|
465
|
-
const decodedEntry = decodeEntryIdb(cursor.value)
|
|
466
|
-
if (
|
|
467
|
-
decodedEntry.event === originEntry.event &&
|
|
468
|
-
decodedEntry.primaryKey === originEntry.primaryKey
|
|
469
|
-
) {
|
|
470
|
-
conflicts.push(decodedEntry)
|
|
471
|
-
}
|
|
472
|
-
cursor.continue()
|
|
473
|
-
}
|
|
474
|
-
tx.oncomplete = () => resume(Effect.void)
|
|
475
|
-
tx.onerror = () =>
|
|
476
|
-
resume(Effect.fail(new EventJournalError({ method: "writeFromRemote", cause: tx.error })))
|
|
477
|
-
return Effect.sync(() => tx.abort())
|
|
478
|
-
})
|
|
479
|
-
|
|
480
|
-
yield* options.effect({ entry: originEntry, conflicts })
|
|
481
|
-
}
|
|
493
|
+
: uncommitted
|
|
482
494
|
|
|
495
|
+
for (const originEntry of compacted) {
|
|
496
|
+
const conflicts: Array<Entry> = []
|
|
483
497
|
yield* Effect.callback<void, EventJournalError>((resume) => {
|
|
484
|
-
const tx = db.transaction(
|
|
498
|
+
const tx = db.transaction("entries", "readonly")
|
|
485
499
|
const entries = tx.objectStore("entries")
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
500
|
+
const cursorRequest = entries.index("id").openCursor(
|
|
501
|
+
IDBKeyRange.lowerBound(originEntry.id as IDBValidKey, true),
|
|
502
|
+
"next"
|
|
503
|
+
)
|
|
504
|
+
cursorRequest.onsuccess = () => {
|
|
505
|
+
const cursor = cursorRequest.result
|
|
506
|
+
if (!cursor) return
|
|
507
|
+
const decodedEntry = decodeEntryIdb(cursor.value)
|
|
508
|
+
if (
|
|
509
|
+
decodedEntry.event === originEntry.event &&
|
|
510
|
+
decodedEntry.primaryKey === originEntry.primaryKey
|
|
511
|
+
) {
|
|
512
|
+
conflicts.push(decodedEntry)
|
|
513
|
+
}
|
|
514
|
+
cursor.continue()
|
|
494
515
|
}
|
|
495
516
|
tx.oncomplete = () => resume(Effect.void)
|
|
496
517
|
tx.onerror = () =>
|
|
497
518
|
resume(Effect.fail(new EventJournalError({ method: "writeFromRemote", cause: tx.error })))
|
|
498
519
|
return Effect.sync(() => tx.abort())
|
|
499
520
|
})
|
|
521
|
+
|
|
522
|
+
yield* options.effect({ entry: originEntry, conflicts })
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
yield* Effect.callback<void, EventJournalError>((resume) => {
|
|
526
|
+
const tx = db.transaction(["entries", "remotes"], "readwrite")
|
|
527
|
+
const entries = tx.objectStore("entries")
|
|
528
|
+
const remotes = tx.objectStore("remotes")
|
|
529
|
+
for (const remoteEntry of uncommittedRemotes) {
|
|
530
|
+
entries.add(encodeEntryIdb(remoteEntry.entry))
|
|
531
|
+
remotes.put({
|
|
532
|
+
remoteId: options.remoteId,
|
|
533
|
+
entryId: remoteEntry.entry.id,
|
|
534
|
+
sequence: remoteEntry.remoteSequence
|
|
535
|
+
})
|
|
536
|
+
}
|
|
537
|
+
tx.oncomplete = () => resume(Effect.void)
|
|
538
|
+
tx.onerror = () => resume(Effect.fail(new EventJournalError({ method: "writeFromRemote", cause: tx.error })))
|
|
539
|
+
return Effect.sync(() => tx.abort())
|
|
540
|
+
})
|
|
541
|
+
return {
|
|
542
|
+
duplicateEntries
|
|
500
543
|
}
|
|
501
544
|
}),
|
|
502
545
|
withRemoteUncommited: (remoteId, f) =>
|
|
@@ -570,10 +613,35 @@ export const makeIndexedDb = (options?: {
|
|
|
570
613
|
changes: PubSub.subscribe(pubsub),
|
|
571
614
|
destroy: Effect.sync(() => {
|
|
572
615
|
indexedDB.deleteDatabase(database)
|
|
573
|
-
})
|
|
616
|
+
}),
|
|
617
|
+
withLock: yield* makeBrowserWithLock(database)
|
|
574
618
|
})
|
|
575
619
|
})
|
|
576
620
|
|
|
621
|
+
const makeBrowserWithLock = Effect.fnUntraced(function*(key: string) {
|
|
622
|
+
if (typeof navigator !== "undefined" && "locks" in navigator) {
|
|
623
|
+
return (storeId: StoreId) => <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
624
|
+
Effect.callback<A, E, R>((resume, signal) => {
|
|
625
|
+
navigator.locks.request(`${key}/${storeId}`, { signal }, () =>
|
|
626
|
+
new Promise<void>((resolve) => {
|
|
627
|
+
resume(Effect.onExit(self, () => {
|
|
628
|
+
resolve()
|
|
629
|
+
return Effect.void
|
|
630
|
+
}))
|
|
631
|
+
})).catch((defect) => resume(Effect.die(defect)))
|
|
632
|
+
})
|
|
633
|
+
}
|
|
634
|
+
const semaphores = new Map<StoreId, Semaphore.Semaphore>()
|
|
635
|
+
return (storeId: StoreId) => {
|
|
636
|
+
let semaphore = semaphores.get(storeId)
|
|
637
|
+
if (!semaphore) {
|
|
638
|
+
semaphore = Semaphore.makeUnsafe(1)
|
|
639
|
+
semaphores.set(storeId, semaphore)
|
|
640
|
+
}
|
|
641
|
+
return semaphore.withPermit
|
|
642
|
+
}
|
|
643
|
+
})
|
|
644
|
+
|
|
577
645
|
const decodeEntryIdb = Schema.decodeSync(Entry)
|
|
578
646
|
const encodeEntryIdb = Schema.encodeSync(Entry)
|
|
579
647
|
const EntryIdbArray = Schema.Array(Entry)
|