@rikalabs/akter 0.2.1 → 0.2.2
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/CHANGELOG.md +8 -0
- package/dist/serve/handlers.d.ts +48 -48
- package/dist/snapshots/forks.js +15 -0
- package/dist/store/log.d.ts +6 -0
- package/dist/store/log.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.2 (2026-10-15)
|
|
4
|
+
|
|
5
|
+
The framework and CLI release together at `0.2.2` on the `latest` npm dist-tag. Stores written by 0.2.0 and 0.2.1 open unchanged.
|
|
6
|
+
|
|
7
|
+
- A fork of a position older than every retained checkpoint is refused with `ForkRefused("retention")` once retention has trimmed its log prefix. It was reported as `CorruptSegment`, as if the store had lost data. A segment missing inside retention is still `CorruptSegment`.
|
|
8
|
+
- The retention refusal is decided from the newest position the oldest retained checkpoint covers (including a fork installed into its shard), so losing in-window segments through that checkpoint is still `CorruptSegment`.
|
|
9
|
+
- New public field: `HistoryCheckpoint` (exported from `@rikalabs/akter/server`) gains a required `expired` boolean. `Store.history.checkpoints` sets it when the checkpoint's offset precedes the oldest stored segment written within retention. It is informational; forks do not use it.
|
|
10
|
+
|
|
3
11
|
## 0.2.1 (2026-10-14)
|
|
4
12
|
|
|
5
13
|
The framework and CLI release together at `0.2.1` on the `latest` npm dist-tag. Stores written by 0.2.0 open unchanged, and a publication epoch 0.2.0 left stranded expires on its own once a 0.2.1 runner or collector reaches it. Do not run `Store.collect` against a store while a 0.2.0 runner of it is still live.
|
package/dist/serve/handlers.d.ts
CHANGED
|
@@ -16,7 +16,12 @@ export declare const STREAM_BUFFER = 64;
|
|
|
16
16
|
declare const make: (engine: Engine, actors: ReadonlyArray<AnyActor>, sessions: Sessions, routing?: Sharding["Service"]) => {
|
|
17
17
|
[k: string]: Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
18
18
|
readonly id: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
19
|
-
|
|
19
|
+
readonly commandId: Schema.String;
|
|
20
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
21
|
+
}>, Schema.Struct<{
|
|
22
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
23
|
+
readonly replayed: Schema.Boolean;
|
|
24
|
+
}>, Schema.Union<readonly [Schema.decodeTo<typeof ActorError, Schema.TaggedStruct<"ActorError", {
|
|
20
25
|
readonly reason: Schema.toTaggedUnion<"_tag", readonly [Schema.TaggedStruct<"Unauthorized", {
|
|
21
26
|
readonly code: Schema.Literals<readonly ["missing_credentials", "invalid_credentials", "expired", "access_denied", "outcome_access_denied", "reauthorization_unavailable"]>;
|
|
22
27
|
}>, Schema.TaggedStruct<"InvalidInput", {
|
|
@@ -46,31 +51,12 @@ declare const make: (engine: Engine, actors: ReadonlyArray<AnyActor>, sessions:
|
|
|
46
51
|
}>]>;
|
|
47
52
|
readonly isRetryable: Schema.Boolean;
|
|
48
53
|
readonly retryAfter: Schema.optionalKey<Schema.Finite>;
|
|
49
|
-
}>, never, never
|
|
54
|
+
}>, never, never>, Schema.TaggedStruct<"ActionFailed", {
|
|
55
|
+
readonly error: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
56
|
+
readonly replayed: Schema.Boolean;
|
|
57
|
+
}>]>, typeof Authorization, never>, any> | Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
50
58
|
readonly id: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
51
|
-
}>, import("effect/rpc/RpcSchema").Stream<Schema.
|
|
52
|
-
readonly kind: Schema.Literal<"Frame">;
|
|
53
|
-
readonly version: Schema.Finite;
|
|
54
|
-
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
55
|
-
}>, Schema.Struct<{
|
|
56
|
-
readonly kind: Schema.Literal<"Patch">;
|
|
57
|
-
readonly version: Schema.Finite;
|
|
58
|
-
readonly ops: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
59
|
-
readonly op: Schema.Literal<"add">;
|
|
60
|
-
readonly path: Schema.String;
|
|
61
|
-
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
62
|
-
readonly description: Schema.optionalKey<Schema.String>;
|
|
63
|
-
}>, Schema.Struct<{
|
|
64
|
-
readonly op: Schema.Literal<"remove">;
|
|
65
|
-
readonly path: Schema.String;
|
|
66
|
-
readonly description: Schema.optionalKey<Schema.String>;
|
|
67
|
-
}>, Schema.Struct<{
|
|
68
|
-
readonly op: Schema.Literal<"replace">;
|
|
69
|
-
readonly path: Schema.String;
|
|
70
|
-
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
71
|
-
readonly description: Schema.optionalKey<Schema.String>;
|
|
72
|
-
}>]>>;
|
|
73
|
-
}>]>, Schema.decodeTo<typeof ActorError, Schema.TaggedStruct<"ActorError", {
|
|
59
|
+
}>, import("effect/rpc/RpcSchema").Stream<Schema.Codec<Schema.Json, Schema.Json, never, never>, Schema.decodeTo<typeof ActorError, Schema.TaggedStruct<"ActorError", {
|
|
74
60
|
readonly reason: Schema.toTaggedUnion<"_tag", readonly [Schema.TaggedStruct<"Unauthorized", {
|
|
75
61
|
readonly code: Schema.Literals<readonly ["missing_credentials", "invalid_credentials", "expired", "access_denied", "outcome_access_denied", "reauthorization_unavailable"]>;
|
|
76
62
|
}>, Schema.TaggedStruct<"InvalidInput", {
|
|
@@ -102,7 +88,6 @@ declare const make: (engine: Engine, actors: ReadonlyArray<AnyActor>, sessions:
|
|
|
102
88
|
readonly retryAfter: Schema.optionalKey<Schema.Finite>;
|
|
103
89
|
}>, never, never>>, Schema.Never, typeof Authorization, never>, any> | Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
104
90
|
readonly id: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
105
|
-
readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
106
91
|
}>, import("effect/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
|
|
107
92
|
readonly kind: Schema.Literal<"Frame">;
|
|
108
93
|
readonly version: Schema.Finite;
|
|
@@ -157,10 +142,7 @@ declare const make: (engine: Engine, actors: ReadonlyArray<AnyActor>, sessions:
|
|
|
157
142
|
readonly retryAfter: Schema.optionalKey<Schema.Finite>;
|
|
158
143
|
}>, never, never>>, Schema.Never, typeof Authorization, never>, any> | Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
159
144
|
readonly id: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
160
|
-
readonly
|
|
161
|
-
readonly to: Schema.optional<Schema.Union<readonly [Schema.String, Schema.Finite]>>;
|
|
162
|
-
readonly limit: Schema.optional<Schema.Int>;
|
|
163
|
-
readonly reverse: Schema.optional<Schema.Boolean>;
|
|
145
|
+
readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
164
146
|
}>, import("effect/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
|
|
165
147
|
readonly kind: Schema.Literal<"Frame">;
|
|
166
148
|
readonly version: Schema.Finite;
|
|
@@ -215,8 +197,11 @@ declare const make: (engine: Engine, actors: ReadonlyArray<AnyActor>, sessions:
|
|
|
215
197
|
readonly retryAfter: Schema.optionalKey<Schema.Finite>;
|
|
216
198
|
}>, never, never>>, Schema.Never, typeof Authorization, never>, any> | Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
217
199
|
readonly id: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
218
|
-
readonly
|
|
219
|
-
|
|
200
|
+
readonly from: Schema.optional<Schema.Union<readonly [Schema.String, Schema.Finite]>>;
|
|
201
|
+
readonly to: Schema.optional<Schema.Union<readonly [Schema.String, Schema.Finite]>>;
|
|
202
|
+
readonly limit: Schema.optional<Schema.Int>;
|
|
203
|
+
readonly reverse: Schema.optional<Schema.Boolean>;
|
|
204
|
+
}>, Schema.$Array<Schema.Codec<Schema.Json, Schema.Json, never, never>>, Schema.decodeTo<typeof ActorError, Schema.TaggedStruct<"ActorError", {
|
|
220
205
|
readonly reason: Schema.toTaggedUnion<"_tag", readonly [Schema.TaggedStruct<"Unauthorized", {
|
|
221
206
|
readonly code: Schema.Literals<readonly ["missing_credentials", "invalid_credentials", "expired", "access_denied", "outcome_access_denied", "reauthorization_unavailable"]>;
|
|
222
207
|
}>, Schema.TaggedStruct<"InvalidInput", {
|
|
@@ -246,14 +231,35 @@ declare const make: (engine: Engine, actors: ReadonlyArray<AnyActor>, sessions:
|
|
|
246
231
|
}>]>;
|
|
247
232
|
readonly isRetryable: Schema.Boolean;
|
|
248
233
|
readonly retryAfter: Schema.optionalKey<Schema.Finite>;
|
|
249
|
-
}>, never, never
|
|
234
|
+
}>, never, never>, typeof Authorization, never>, any> | Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
250
235
|
readonly id: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
251
|
-
readonly
|
|
252
|
-
readonly
|
|
253
|
-
|
|
236
|
+
readonly from: Schema.optional<Schema.Union<readonly [Schema.String, Schema.Finite]>>;
|
|
237
|
+
readonly to: Schema.optional<Schema.Union<readonly [Schema.String, Schema.Finite]>>;
|
|
238
|
+
readonly limit: Schema.optional<Schema.Int>;
|
|
239
|
+
readonly reverse: Schema.optional<Schema.Boolean>;
|
|
240
|
+
}>, import("effect/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
|
|
241
|
+
readonly kind: Schema.Literal<"Frame">;
|
|
242
|
+
readonly version: Schema.Finite;
|
|
254
243
|
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
255
|
-
|
|
256
|
-
|
|
244
|
+
}>, Schema.Struct<{
|
|
245
|
+
readonly kind: Schema.Literal<"Patch">;
|
|
246
|
+
readonly version: Schema.Finite;
|
|
247
|
+
readonly ops: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
248
|
+
readonly op: Schema.Literal<"add">;
|
|
249
|
+
readonly path: Schema.String;
|
|
250
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
251
|
+
readonly description: Schema.optionalKey<Schema.String>;
|
|
252
|
+
}>, Schema.Struct<{
|
|
253
|
+
readonly op: Schema.Literal<"remove">;
|
|
254
|
+
readonly path: Schema.String;
|
|
255
|
+
readonly description: Schema.optionalKey<Schema.String>;
|
|
256
|
+
}>, Schema.Struct<{
|
|
257
|
+
readonly op: Schema.Literal<"replace">;
|
|
258
|
+
readonly path: Schema.String;
|
|
259
|
+
readonly value: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
260
|
+
readonly description: Schema.optionalKey<Schema.String>;
|
|
261
|
+
}>]>>;
|
|
262
|
+
}>]>, Schema.decodeTo<typeof ActorError, Schema.TaggedStruct<"ActorError", {
|
|
257
263
|
readonly reason: Schema.toTaggedUnion<"_tag", readonly [Schema.TaggedStruct<"Unauthorized", {
|
|
258
264
|
readonly code: Schema.Literals<readonly ["missing_credentials", "invalid_credentials", "expired", "access_denied", "outcome_access_denied", "reauthorization_unavailable"]>;
|
|
259
265
|
}>, Schema.TaggedStruct<"InvalidInput", {
|
|
@@ -283,16 +289,10 @@ declare const make: (engine: Engine, actors: ReadonlyArray<AnyActor>, sessions:
|
|
|
283
289
|
}>]>;
|
|
284
290
|
readonly isRetryable: Schema.Boolean;
|
|
285
291
|
readonly retryAfter: Schema.optionalKey<Schema.Finite>;
|
|
286
|
-
}>, never, never
|
|
287
|
-
readonly error: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
288
|
-
readonly replayed: Schema.Boolean;
|
|
289
|
-
}>]>, typeof Authorization, never>, any> | Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
292
|
+
}>, never, never>>, Schema.Never, typeof Authorization, never>, any> | Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
290
293
|
readonly id: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
291
|
-
readonly
|
|
292
|
-
|
|
293
|
-
readonly limit: Schema.optional<Schema.Int>;
|
|
294
|
-
readonly reverse: Schema.optional<Schema.Boolean>;
|
|
295
|
-
}>, Schema.$Array<Schema.Codec<Schema.Json, Schema.Json, never, never>>, Schema.decodeTo<typeof ActorError, Schema.TaggedStruct<"ActorError", {
|
|
294
|
+
readonly presence: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
295
|
+
}>, import("effect/rpc/RpcSchema").Stream<Schema.Literal<"joined">, Schema.decodeTo<typeof ActorError, Schema.TaggedStruct<"ActorError", {
|
|
296
296
|
readonly reason: Schema.toTaggedUnion<"_tag", readonly [Schema.TaggedStruct<"Unauthorized", {
|
|
297
297
|
readonly code: Schema.Literals<readonly ["missing_credentials", "invalid_credentials", "expired", "access_denied", "outcome_access_denied", "reauthorization_unavailable"]>;
|
|
298
298
|
}>, Schema.TaggedStruct<"InvalidInput", {
|
|
@@ -322,7 +322,7 @@ declare const make: (engine: Engine, actors: ReadonlyArray<AnyActor>, sessions:
|
|
|
322
322
|
}>]>;
|
|
323
323
|
readonly isRetryable: Schema.Boolean;
|
|
324
324
|
readonly retryAfter: Schema.optionalKey<Schema.Finite>;
|
|
325
|
-
}>, never, never
|
|
325
|
+
}>, never, never>>, Schema.Never, typeof Authorization, never>, any> | Rpc.ToHandlerFn<Rpc.Rpc<string, Schema.Struct<{
|
|
326
326
|
readonly where: Schema.$Record<Schema.String, Schema.Struct<{
|
|
327
327
|
readonly eq: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
|
328
328
|
readonly gt: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
package/dist/snapshots/forks.js
CHANGED
|
@@ -18,6 +18,17 @@ export class ForkRefused extends Schema.TaggedError()("ForkRefused", {
|
|
|
18
18
|
}) {
|
|
19
19
|
}
|
|
20
20
|
const same = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
21
|
+
/**
|
|
22
|
+
* A position no retained checkpoint can serve precedes the oldest one. When the newest position
|
|
23
|
+
* that checkpoint covers is older than the fork's retention cutoff, every position through it, the
|
|
24
|
+
* requested one included, is outside retention and its log prefix may already be trimmed, so the
|
|
25
|
+
* fork is refused for retention without reading the log. That time is the later of the shard's last
|
|
26
|
+
* action and the latest position any actor holds, because a fork installed into a shard records a
|
|
27
|
+
* position without advancing the shard's last action. The checkpoint's own contents decide this, on
|
|
28
|
+
* the same clock as the admission check, because which segments survive says nothing about
|
|
29
|
+
* retention once in-window segments are lost. Otherwise the log is read from its start, and a
|
|
30
|
+
* segment missing there is corruption.
|
|
31
|
+
*/
|
|
21
32
|
const forkOnce = (target, options, source = target.store) => Effect.gen(function* () {
|
|
22
33
|
const { actor, from, at, grant } = options;
|
|
23
34
|
const tenant = options.tenant ?? "default";
|
|
@@ -45,8 +56,10 @@ const forkOnce = (target, options, source = target.store) => Effect.gen(function
|
|
|
45
56
|
const cache = new PageCache();
|
|
46
57
|
const available = yield* source.history.checkpoints(shard);
|
|
47
58
|
let base;
|
|
59
|
+
let oldestAt;
|
|
48
60
|
for (const retained of available) {
|
|
49
61
|
const decoded = Result.getOrThrow(Checkpoints.decode(yield* PagedCheckpoints.read(retained.statePages[0], source)));
|
|
62
|
+
oldestAt ??= decoded.actors.reduce((latest, candidate) => Math.max(latest, candidate.position?.at ?? 0), decoded.lastAt);
|
|
50
63
|
const snapshot = decoded.actors.find((candidate) => candidate.actor === actor.name && same(candidate.key, from));
|
|
51
64
|
const suitable = "offset" in at
|
|
52
65
|
? decoded.offset < at.offset
|
|
@@ -56,6 +69,8 @@ const forkOnce = (target, options, source = target.store) => Effect.gen(function
|
|
|
56
69
|
if (suitable && (base === undefined || decoded.offset > base.offset))
|
|
57
70
|
base = decoded;
|
|
58
71
|
}
|
|
72
|
+
if (base === undefined && oldestAt !== undefined && oldestAt < cutoff)
|
|
73
|
+
return yield* ForkRefused.make({ reason: "retention" });
|
|
59
74
|
const image = base === undefined ? Durable.empty(shard) : Checkpoints.restore(catalog, base, cache);
|
|
60
75
|
const start = base?.offset ?? 0;
|
|
61
76
|
const initial = image.actors.get(Durable.addressOf(actor.name, from));
|
package/dist/store/log.d.ts
CHANGED
|
@@ -38,6 +38,12 @@ export interface StoredSegment extends Segment {
|
|
|
38
38
|
/** A retained checkpoint and its provider's last-modified time in epoch milliseconds. */
|
|
39
39
|
export interface HistoryCheckpoint extends Manifest {
|
|
40
40
|
readonly modified: number;
|
|
41
|
+
/**
|
|
42
|
+
* The checkpoint's offset precedes the oldest stored segment written within retention, or no
|
|
43
|
+
* stored segment was written within it. Lost in-window segments move that boundary later, so
|
|
44
|
+
* this does not prove the checkpoint's positions are outside retention.
|
|
45
|
+
*/
|
|
46
|
+
readonly expired: boolean;
|
|
41
47
|
}
|
|
42
48
|
/**
|
|
43
49
|
* The bucket log protocol. A segment is created once at `head + 1`; the
|
package/dist/store/log.js
CHANGED
|
@@ -248,7 +248,11 @@ export const bucketLog = (settings) => {
|
|
|
248
248
|
key,
|
|
249
249
|
reason: "checkpoint offset differs from its key",
|
|
250
250
|
});
|
|
251
|
-
retained.push({
|
|
251
|
+
retained.push({
|
|
252
|
+
...manifest,
|
|
253
|
+
modified,
|
|
254
|
+
expired: window.first === undefined || offset < window.first,
|
|
255
|
+
});
|
|
252
256
|
}
|
|
253
257
|
return retained.reverse();
|
|
254
258
|
}).pipe(Effect.catchTag("StorageUnavailable", (error) => Effect.fail(unavailable(error))));
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@rikalabs/akter","version":"0.2.
|
|
1
|
+
{"name":"@rikalabs/akter","version":"0.2.2","files":["dist","CHANGELOG.md","README.md","LICENSE","NOTICE"],"dependencies":{"@aws-sdk/client-s3":"3.1148.0","@effect/platform-node-shared":"4.0.3"},"peerDependencies":{"@effect/platform-bun":"^4.0.3","@effect/platform-node":"^4.0.3","effect":"^4.0.3"},"publishConfig":{"access":"public"},"description":"The framework for durable, stateful backends that power realtime apps, background work, and agents.","keywords":["actor-model","actors","agents","background-jobs","bun","durable","effect","effect-ts","realtime","stateful","transactions","workflows"],"homepage":"https://github.com/Rika-Labs/akter#readme","bugs":"https://github.com/Rika-Labs/akter/issues","license":"Apache-2.0","author":"Rika Labs","repository":{"type":"git","url":"git+https://github.com/Rika-Labs/akter.git","directory":"packages/akter"},"type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./server":{"types":"./dist/server.d.ts","default":"./dist/server.js"},"./client":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"./testing":{"types":"./dist/testing/index.d.ts","default":"./dist/testing/index.js"},"./package.json":"./package.json"},"peerDependenciesMeta":{"@effect/platform-bun":{"optional":true},"@effect/platform-node":{"optional":true}},"engines":{"bun":">=1.4.2","node":">=24.0.0"}}
|