@relayfx/test 0.2.16 → 0.3.0

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/index.js CHANGED
@@ -4093,6 +4093,7 @@ var exports_execution_event_repository = {};
4093
4093
  __export(exports_execution_event_repository, {
4094
4094
  testLayer: () => testLayer12,
4095
4095
  sumUsage: () => sumUsage,
4096
+ page: () => page,
4096
4097
  notifyAppended: () => notifyAppended,
4097
4098
  memoryLayer: () => memoryLayer12,
4098
4099
  maxSequence: () => maxSequence,
@@ -4195,7 +4196,20 @@ var ExecutionEventRow = Schema30.Struct({
4195
4196
  created_at: Schema30.Union([Schema30.Date, Schema30.String, Schema30.Finite])
4196
4197
  }).annotate({ identifier: "Relay.ExecutionEvent.Row" });
4197
4198
  var maxListLimit = 1000;
4199
+ var defaultPageLimit = 256;
4198
4200
  var listLimit = (input) => input.limit ?? maxListLimit;
4201
+ var pageLimit = (input) => Math.max(1, Math.min(maxListLimit, Math.trunc(input.limit ?? defaultPageLimit)));
4202
+ var pageResult = (events, limit, direction) => {
4203
+ const hasMore = events.length > limit;
4204
+ const retained = events.slice(0, limit);
4205
+ const ordered = direction === "backward" ? retained.toReversed() : retained;
4206
+ return {
4207
+ events: ordered,
4208
+ hasMore,
4209
+ ...ordered[0] === undefined ? {} : { oldestCursor: ordered[0].cursor },
4210
+ ...ordered.at(-1) === undefined ? {} : { newestCursor: ordered.at(-1).cursor }
4211
+ };
4212
+ };
4199
4213
  var usageReportedEventType = "model.usage.reported";
4200
4214
  var eventUsageTokens = (event) => {
4201
4215
  if (event.type !== usageReportedEventType)
@@ -4487,6 +4501,42 @@ var layer12 = Layer13.effect(Service13, Effect14.gen(function* () {
4487
4501
  const rows = yield* selectAfterSequence(tenantId, input).pipe(Effect14.mapError(toRepositoryError8));
4488
4502
  return yield* decodeEvents(rows);
4489
4503
  });
4504
+ const page = Effect14.fn("ExecutionEventRepository.page")(function* (input) {
4505
+ const tenantId = yield* current();
4506
+ const cursor = input.direction === "forward" ? input.afterCursor : input.beforeCursor;
4507
+ const cursorRows = cursor === undefined ? [] : yield* sql`
4508
+ SELECT ${columns}
4509
+ FROM relay_execution_events
4510
+ WHERE tenant_id = ${tenantId} AND execution_id = ${input.executionId}
4511
+ AND ${sql.literal(cursorColumn)} = ${cursor}
4512
+ LIMIT 1
4513
+ `.pipe(Effect14.mapError(toRepositoryError8));
4514
+ const cursorRow = cursorRows[0];
4515
+ if (cursor !== undefined && cursorRow === undefined) {
4516
+ return yield* ExecutionEventCursorNotFound.make({
4517
+ execution_id: input.executionId,
4518
+ cursor
4519
+ });
4520
+ }
4521
+ const boundary = cursorRow === undefined ? undefined : toEvent(yield* decodeRow7(cursorRow)).sequence;
4522
+ const limit = pageLimit(input);
4523
+ const rows = input.direction === "forward" ? yield* sql`
4524
+ SELECT ${columns}
4525
+ FROM relay_execution_events
4526
+ WHERE tenant_id = ${tenantId} AND execution_id = ${input.executionId}
4527
+ ${boundary === undefined ? sql`` : sql`AND sequence > ${boundary}`}
4528
+ ORDER BY sequence ASC
4529
+ LIMIT ${sql.literal(String(limit + 1))}
4530
+ `.pipe(Effect14.mapError(toRepositoryError8)) : yield* sql`
4531
+ SELECT ${columns}
4532
+ FROM relay_execution_events
4533
+ WHERE tenant_id = ${tenantId} AND execution_id = ${input.executionId}
4534
+ ${boundary === undefined ? sql`` : sql`AND sequence < ${boundary}`}
4535
+ ORDER BY sequence DESC
4536
+ LIMIT ${sql.literal(String(limit + 1))}
4537
+ `.pipe(Effect14.mapError(toRepositoryError8));
4538
+ return pageResult(yield* decodeEvents(rows), limit, input.direction);
4539
+ });
4490
4540
  return Service13.of({
4491
4541
  signalTransport: bus.transport,
4492
4542
  append,
@@ -4499,7 +4549,8 @@ var layer12 = Layer13.effect(Service13, Effect14.gen(function* () {
4499
4549
  sumUsage,
4500
4550
  notifyAppended,
4501
4551
  appendedSignals,
4502
- listAfterSequence
4552
+ listAfterSequence,
4553
+ page
4503
4554
  });
4504
4555
  }));
4505
4556
  var layerWithBus = layer12.pipe(Layer13.provide(layerFromDialect));
@@ -4561,10 +4612,24 @@ var memoryLayer12 = Layer13.effect(Service13, Effect14.gen(function* () {
4561
4612
  listAfterSequence: Effect14.fn("ExecutionEventRepository.memory.listAfterSequence")((input) => {
4562
4613
  const executionEvents = events.get(input.executionId) ?? [];
4563
4614
  return Effect14.succeed(executionEvents.filter((event) => input.afterSequence === undefined || event.sequence > input.afterSequence).toSorted((left, right) => left.sequence - right.sequence).slice(0, input.limit));
4615
+ }),
4616
+ page: Effect14.fn("ExecutionEventRepository.memory.page")(function* (input) {
4617
+ const executionEvents = (events.get(input.executionId) ?? []).toSorted((left, right) => left.sequence - right.sequence);
4618
+ const cursor = input.direction === "forward" ? input.afterCursor : input.beforeCursor;
4619
+ const boundary = cursor === undefined ? undefined : executionEvents.findIndex((event) => event.cursor === cursor);
4620
+ if (cursor !== undefined && boundary !== undefined && boundary < 0) {
4621
+ return yield* ExecutionEventCursorNotFound.make({ execution_id: input.executionId, cursor });
4622
+ }
4623
+ const limit = pageLimit(input);
4624
+ const candidates = input.direction === "forward" ? executionEvents.slice(boundary === undefined ? 0 : boundary + 1, (boundary ?? -1) + limit + 2) : executionEvents.slice(0, boundary ?? executionEvents.length).toReversed().slice(0, limit + 1);
4625
+ return pageResult(candidates, limit, input.direction);
4564
4626
  })
4565
4627
  });
4566
4628
  }));
4567
- var testLayer12 = (implementation) => Layer13.succeed(Service13, Service13.of(implementation));
4629
+ var testLayer12 = (implementation) => Layer13.succeed(Service13, Service13.of({
4630
+ ...implementation,
4631
+ page: implementation.page ?? (() => Effect14.succeed({ events: [], hasMore: false }))
4632
+ }));
4568
4633
  var append = Effect14.fn("ExecutionEventRepository.append.call")(function* (input) {
4569
4634
  const repository = yield* Service13;
4570
4635
  return yield* repository.append(input);
@@ -4573,6 +4638,10 @@ var list4 = Effect14.fn("ExecutionEventRepository.list.call")(function* (input)
4573
4638
  const repository = yield* Service13;
4574
4639
  return yield* repository.list(input);
4575
4640
  });
4641
+ var page = Effect14.fn("ExecutionEventRepository.page.call")(function* (input) {
4642
+ const repository = yield* Service13;
4643
+ return yield* repository.page(input);
4644
+ });
4576
4645
  var findBySequenceOrCursor = Effect14.fn("ExecutionEventRepository.findBySequenceOrCursor.call")(function* (input) {
4577
4646
  const repository = yield* Service13;
4578
4647
  return yield* repository.findBySequenceOrCursor(input);
@@ -4866,10 +4935,10 @@ var layerWithoutBus = Layer14.effect(Service14, Effect15.gen(function* () {
4866
4935
  ${cursor === undefined ? sql`` : sql`AND (updated_at > ${timestampParam(sql, cursor.updatedAt)} OR (updated_at = ${timestampParam(sql, cursor.updatedAt)} AND id > ${cursor.id}))`}
4867
4936
  ORDER BY updated_at ASC, id ASC LIMIT ${sql.literal(String(pageSize))}
4868
4937
  `.pipe(Effect15.mapError(toRepositoryError9));
4869
- const page = (yield* Effect15.forEach(rows, decodeRow8)).map(toRecord6);
4870
- records.push(...page);
4871
- const last = page.at(-1);
4872
- if (last === undefined || page.length < pageSize)
4938
+ const page2 = (yield* Effect15.forEach(rows, decodeRow8)).map(toRecord6);
4939
+ records.push(...page2);
4940
+ const last = page2.at(-1);
4941
+ if (last === undefined || page2.length < pageSize)
4873
4942
  break;
4874
4943
  cursor = { updatedAt: last.updatedAt, id: last.id };
4875
4944
  }
@@ -4913,9 +4982,9 @@ var memoryLayer13 = Layer14.sync(Service14, () => {
4913
4982
  return yield* Effect15.sync(() => {
4914
4983
  const limit = listLimit2(input);
4915
4984
  const matched = Array.from(records.values()).filter((record2) => input.rootAddressId === undefined || record2.rootAddressId === input.rootAddressId).filter((record2) => input.sessionId === undefined || record2.sessionId === input.sessionId).filter((record2) => input.status === undefined || record2.status === input.status).toSorted(compareDesc).filter((record2) => afterCursor(record2, input.cursor));
4916
- const page = matched.slice(0, limit).map(cloneRecord4);
4917
- const nextCursor = nextCursorOf(page, matched.length, limit);
4918
- return nextCursor === undefined ? { records: page } : { records: page, nextCursor };
4985
+ const page2 = matched.slice(0, limit).map(cloneRecord4);
4986
+ const nextCursor = nextCursorOf(page2, matched.length, limit);
4987
+ return nextCursor === undefined ? { records: page2 } : { records: page2, nextCursor };
4919
4988
  });
4920
4989
  }),
4921
4990
  transition: Effect15.fn("ExecutionRepository.memory.transition")(function* (input) {
@@ -7285,9 +7354,9 @@ var memoryLayer22 = Layer23.sync(Service23, () => {
7285
7354
  list: Effect24.fn("SessionRepository.memory.list")((input) => Effect24.sync(() => {
7286
7355
  const limit = listLimit3(input);
7287
7356
  const matched = Array.from(sessions.values()).filter((record2) => input.rootAddressId === undefined || record2.rootAddressId === input.rootAddressId).toSorted(compareDesc2).filter((record2) => afterCursor2(record2, input.cursor));
7288
- const page = matched.slice(0, limit).map(cloneSession);
7289
- const nextCursor = nextCursorOf2(page, matched.length, limit);
7290
- return nextCursor === undefined ? { records: page } : { records: page, nextCursor };
7357
+ const page2 = matched.slice(0, limit).map(cloneSession);
7358
+ const nextCursor = nextCursorOf2(page2, matched.length, limit);
7359
+ return nextCursor === undefined ? { records: page2 } : { records: page2, nextCursor };
7291
7360
  })),
7292
7361
  touch: Effect24.fn("SessionRepository.memory.touch")(function* (input) {
7293
7362
  const current2 = sessions.get(input.id);
@@ -10320,7 +10389,27 @@ class EventLogError extends Schema47.TaggedErrorClass()("EventLogError", {
10320
10389
  class Service30 extends Context30.Service()("@relayfx/runtime/execution/event-log-service/Service") {
10321
10390
  }
10322
10391
  var defaultReplayLimit = 1000;
10392
+ var defaultPageLimit2 = 256;
10323
10393
  var limitOf = (input) => input.limit ?? defaultReplayLimit;
10394
+ var pageLimitOf = (input) => Math.max(1, Math.min(defaultReplayLimit, Math.trunc(input.limit ?? defaultPageLimit2)));
10395
+ var pageEvents = (events, input) => {
10396
+ const cursor = input.direction === "forward" ? input.afterCursor : input.beforeCursor;
10397
+ const boundary = cursor === undefined ? undefined : events.findIndex((event) => event.cursor === cursor);
10398
+ if (cursor !== undefined && boundary !== undefined && boundary < 0) {
10399
+ return Effect33.fail(EventLogCursorNotFound.make({ execution_id: input.executionId, cursor }));
10400
+ }
10401
+ const limit = pageLimitOf(input);
10402
+ const candidates = input.direction === "forward" ? events.slice(boundary === undefined ? 0 : boundary + 1, (boundary ?? -1) + limit + 2) : events.slice(0, boundary ?? events.length).toReversed().slice(0, limit + 1);
10403
+ const hasMore = candidates.length > limit;
10404
+ const retained = candidates.slice(0, limit);
10405
+ const ordered = input.direction === "backward" ? retained.toReversed() : retained;
10406
+ return Effect33.succeed({
10407
+ events: ordered,
10408
+ hasMore,
10409
+ ...ordered[0] === undefined ? {} : { oldestCursor: ordered[0].cursor },
10410
+ ...ordered.at(-1) === undefined ? {} : { newestCursor: ordered.at(-1).cursor }
10411
+ });
10412
+ };
10324
10413
  var eventUsageTokens2 = (event) => {
10325
10414
  if (event.type !== "model.usage.reported")
10326
10415
  return 0;
@@ -10468,6 +10557,7 @@ var layerFromRepository2 = Layer30.effect(Service30, Effect33.gen(function* () {
10468
10557
  yield* recordEventReplayed("repository", replay.history.length);
10469
10558
  return replay.history;
10470
10559
  }),
10560
+ page: Effect33.fn("EventLog.repository.page")((input) => repository.page(input).pipe(Effect33.mapError(mapRepositoryListError))),
10471
10561
  stream: (input) => Stream5.unwrap(loadReplay(input).pipe(Effect33.map(({ history, sequence }) => {
10472
10562
  const replay = Stream5.fromIterable(history);
10473
10563
  return history.some(isTerminal) ? replay : replay.pipe(Stream5.concat(liveTail(repository, hubs, input, history.at(-1)?.sequence ?? sequence).pipe(Stream5.takeUntil(isTerminal))));
@@ -10523,6 +10613,10 @@ var memoryLayer28 = Layer30.effect(Service30, Effect33.gen(function* () {
10523
10613
  yield* recordEventReplayed("memory", replay.history.length);
10524
10614
  return replay.history;
10525
10615
  }),
10616
+ page: Effect33.fn("EventLog.memory.page")(function* (input) {
10617
+ const executionEvents = yield* allEvents(input.executionId);
10618
+ return yield* pageEvents(executionEvents, input);
10619
+ }),
10526
10620
  stream: (input) => Stream5.unwrap(loadReplay(input).pipe(Effect33.map(({ history, sequence }) => {
10527
10621
  const replay = Stream5.fromIterable(history);
10528
10622
  return history.some(isTerminal) ? replay : replay.pipe(Stream5.concat(liveEvents(pubsub, input, history, sequence)));
@@ -10577,6 +10671,10 @@ var replay = Effect33.fn("EventLog.replay.call")(function* (input) {
10577
10671
  const service = yield* Service30;
10578
10672
  return yield* service.replay(input);
10579
10673
  });
10674
+ var page2 = Effect33.fn("EventLog.page.call")(function* (input) {
10675
+ const service = yield* Service30;
10676
+ return yield* service.page(input);
10677
+ });
10580
10678
  var findBySequenceOrCursor2 = Effect33.fn("EventLog.findBySequenceOrCursor.call")(function* (input) {
10581
10679
  const service = yield* Service30;
10582
10680
  return yield* service.findBySequenceOrCursor(input);
@@ -19040,7 +19138,7 @@ var layerFromServices2 = Layer86.effect(Service66, Effect97.gen(function* () {
19040
19138
  return Service66.of({
19041
19139
  stream: (input) => Stream14.unwrap(Effect97.gen(function* () {
19042
19140
  const seen = yield* Ref25.make(HashSet8.empty());
19043
- const listAll = (cursor) => repository.list({ sessionId: input.sessionId, limit: 100, ...cursor === undefined ? {} : { cursor } }).pipe(Effect97.flatMap((page) => page.nextCursor === undefined ? Effect97.succeed(page.records) : listAll(page.nextCursor).pipe(Effect97.map((rest) => [...page.records, ...rest]))));
19141
+ const listAll = (cursor) => repository.list({ sessionId: input.sessionId, limit: 100, ...cursor === undefined ? {} : { cursor } }).pipe(Effect97.flatMap((page3) => page3.nextCursor === undefined ? Effect97.succeed(page3.records) : listAll(page3.nextCursor).pipe(Effect97.map((rest) => [...page3.records, ...rest]))));
19044
19142
  const discover = listAll().pipe(Effect97.flatMap((records) => Ref25.modify(seen, (known) => {
19045
19143
  const fresh = records.filter((record2) => !HashSet8.has(known, record2.id));
19046
19144
  return [fresh, fresh.reduce((set, record2) => HashSet8.add(set, record2.id), known)];
@@ -19,6 +19,25 @@ export interface ReplayInput {
19
19
  readonly afterCursor?: string;
20
20
  readonly limit?: number;
21
21
  }
22
+ export type PageInput = {
23
+ readonly executionId: Ids.ExecutionId;
24
+ readonly direction: "forward";
25
+ readonly afterCursor?: string;
26
+ readonly beforeCursor?: never;
27
+ readonly limit?: number;
28
+ } | {
29
+ readonly executionId: Ids.ExecutionId;
30
+ readonly direction: "backward";
31
+ readonly afterCursor?: never;
32
+ readonly beforeCursor?: string;
33
+ readonly limit?: number;
34
+ };
35
+ export interface PageResult {
36
+ readonly events: ReadonlyArray<Execution.ExecutionEvent>;
37
+ readonly hasMore: boolean;
38
+ readonly oldestCursor?: string;
39
+ readonly newestCursor?: string;
40
+ }
22
41
  export interface FindBySequenceOrCursorInput {
23
42
  readonly executionId: Ids.ExecutionId;
24
43
  readonly sequence: Execution.ExecutionEventSequence;
@@ -41,6 +60,7 @@ export interface Interface {
41
60
  readonly append: (input: Execution.ExecutionEvent) => Effect.Effect<Execution.ExecutionEvent, EventLogError>;
42
61
  readonly replay: (input: ReplayInput) => Effect.Effect<ReadonlyArray<Execution.ExecutionEvent>, EventLogCursorNotFound | EventLogError>;
43
62
  readonly stream: (input: ReplayInput) => Stream.Stream<Execution.ExecutionEvent, EventLogCursorNotFound | EventLogError>;
63
+ readonly page: (input: PageInput) => Effect.Effect<PageResult, EventLogCursorNotFound | EventLogError>;
44
64
  readonly findBySequenceOrCursor: (input: FindBySequenceOrCursorInput) => Effect.Effect<Option.Option<Execution.ExecutionEvent>, EventLogError>;
45
65
  readonly findByCursor: (input: FindByCursorInput) => Effect.Effect<Option.Option<Execution.ExecutionEvent>, EventLogError>;
46
66
  readonly findBySequence: (input: FindBySequenceInput) => Effect.Effect<Option.Option<Execution.ExecutionEvent>, EventLogError>;
@@ -53,7 +73,8 @@ export declare class Service extends Service_base {
53
73
  }
54
74
  export declare const layerFromRepository: Layer.Layer<Service, never, ExecutionEventRepository.Service>;
55
75
  export declare const memoryLayer: Layer.Layer<Service, never, never>;
56
- export declare const testLayer: (implementation: Interface) => Layer.Layer<Service, never, never>;
76
+ type TestImplementation = Omit<Interface, "page"> & Partial<Pick<Interface, "page">>;
77
+ export declare const testLayer: (implementation: TestImplementation) => Layer.Layer<Service, never, never>;
57
78
  export declare const appendIdempotentTo: {
58
79
  (eventLog: Interface, input: Execution.ExecutionEvent): Effect.Effect<Execution.ExecutionEvent, EventLogError>;
59
80
  (input: Execution.ExecutionEvent): (eventLog: Interface) => Effect.Effect<Execution.ExecutionEvent, EventLogError>;
@@ -61,6 +82,7 @@ export declare const appendIdempotentTo: {
61
82
  export declare const append: (input: Execution.ExecutionEvent) => Effect.Effect<Execution.ExecutionEvent, EventLogError, Service>;
62
83
  export declare const appendIdempotent: (input: Execution.ExecutionEvent) => Effect.Effect<Execution.ExecutionEvent, EventLogError, Service>;
63
84
  export declare const replay: (input: ReplayInput) => Effect.Effect<readonly Execution.ExecutionEvent[], EventLogCursorNotFound | EventLogError, Service>;
85
+ export declare const page: (input: PageInput) => Effect.Effect<PageResult, EventLogCursorNotFound | EventLogError, Service>;
64
86
  export declare const findBySequenceOrCursor: (input: FindBySequenceOrCursorInput) => Effect.Effect<Option.Option<Execution.ExecutionEvent>, EventLogError, Service>;
65
87
  export declare const findByCursor: (input: FindByCursorInput) => Effect.Effect<Option.Option<Execution.ExecutionEvent>, EventLogError, Service>;
66
88
  export declare const findBySequence: (input: FindBySequenceInput) => Effect.Effect<Option.Option<Execution.ExecutionEvent>, EventLogError, Service>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@relayfx/test",
4
- "version": "0.2.16",
4
+ "version": "0.3.0",
5
5
  "description": "Experimental deterministic test kit for Relay applications",
6
6
  "type": "module",
7
7
  "exports": {
@@ -36,7 +36,7 @@
36
36
  "typecheck": "bun tsc --noEmit"
37
37
  },
38
38
  "dependencies": {
39
- "@relayfx/sdk": "0.2.16",
39
+ "@relayfx/sdk": "0.3.0",
40
40
  "effect": "4.0.0-beta.93"
41
41
  },
42
42
  "peerDependencies": {