@cadenza.io/core 3.26.0 → 3.27.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.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +87 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1993,6 +1993,7 @@ interface ActorSpec<D extends Record<string, any>, R = AnyObject> {
|
|
|
1993
1993
|
interface ActorFactoryOptions<D extends Record<string, any> = Record<string, any>, R = AnyObject> {
|
|
1994
1994
|
isMeta?: boolean;
|
|
1995
1995
|
definitionSource?: ActorDefinition<D, R>;
|
|
1996
|
+
hydrateDurableState?: (actorKey: string) => Promise<ActorDurableStateHydration<D> | null>;
|
|
1996
1997
|
}
|
|
1997
1998
|
/**
|
|
1998
1999
|
* Optional per-binding behavior when wrapping actor handlers.
|
|
@@ -2075,6 +2076,10 @@ interface ActorTaskContext<D extends Record<string, any>, R = AnyObject> {
|
|
|
2075
2076
|
* Handler signature used by `actor.task(...)`.
|
|
2076
2077
|
*/
|
|
2077
2078
|
type ActorTaskHandler<D extends Record<string, any>, R = AnyObject> = (context: ActorTaskContext<D, R>) => TaskResult | ActorStateReducer<D> | Promise<TaskResult | ActorStateReducer<D>>;
|
|
2079
|
+
interface ActorDurableStateHydration<D extends Record<string, any>> {
|
|
2080
|
+
durableState: D;
|
|
2081
|
+
durableVersion: number;
|
|
2082
|
+
}
|
|
2078
2083
|
/**
|
|
2079
2084
|
* Metadata attached to wrapped actor task functions.
|
|
2080
2085
|
*/
|
|
@@ -2101,9 +2106,11 @@ declare class Actor<D extends Record<string, any> = AnyObject, R = AnyObject> {
|
|
|
2101
2106
|
readonly spec: ActorSpec<D, R>;
|
|
2102
2107
|
readonly kind: ActorKind;
|
|
2103
2108
|
private readonly sourceDefinition?;
|
|
2109
|
+
private readonly hydrateDurableState?;
|
|
2104
2110
|
private readonly stateByKey;
|
|
2105
2111
|
private readonly sessionByKey;
|
|
2106
2112
|
private readonly idempotencyByKey;
|
|
2113
|
+
private readonly pendingHydrationByKey;
|
|
2107
2114
|
private readonly writeQueueByKey;
|
|
2108
2115
|
private nextTaskBindingIndex;
|
|
2109
2116
|
/**
|
|
@@ -2154,7 +2161,10 @@ declare class Actor<D extends Record<string, any> = AnyObject, R = AnyObject> {
|
|
|
2154
2161
|
private resolveInitialDurableState;
|
|
2155
2162
|
private resolveInitialRuntimeState;
|
|
2156
2163
|
private ensureStateRecord;
|
|
2164
|
+
private maybeHydrateStateRecord;
|
|
2157
2165
|
private touchSession;
|
|
2166
|
+
private pruneExpiredActorKeys;
|
|
2167
|
+
private isSessionExpired;
|
|
2158
2168
|
private runWithOptionalIdempotency;
|
|
2159
2169
|
private runWithPerKeyWriteSerialization;
|
|
2160
2170
|
private getActiveIdempotencyRecord;
|
package/dist/index.d.ts
CHANGED
|
@@ -1993,6 +1993,7 @@ interface ActorSpec<D extends Record<string, any>, R = AnyObject> {
|
|
|
1993
1993
|
interface ActorFactoryOptions<D extends Record<string, any> = Record<string, any>, R = AnyObject> {
|
|
1994
1994
|
isMeta?: boolean;
|
|
1995
1995
|
definitionSource?: ActorDefinition<D, R>;
|
|
1996
|
+
hydrateDurableState?: (actorKey: string) => Promise<ActorDurableStateHydration<D> | null>;
|
|
1996
1997
|
}
|
|
1997
1998
|
/**
|
|
1998
1999
|
* Optional per-binding behavior when wrapping actor handlers.
|
|
@@ -2075,6 +2076,10 @@ interface ActorTaskContext<D extends Record<string, any>, R = AnyObject> {
|
|
|
2075
2076
|
* Handler signature used by `actor.task(...)`.
|
|
2076
2077
|
*/
|
|
2077
2078
|
type ActorTaskHandler<D extends Record<string, any>, R = AnyObject> = (context: ActorTaskContext<D, R>) => TaskResult | ActorStateReducer<D> | Promise<TaskResult | ActorStateReducer<D>>;
|
|
2079
|
+
interface ActorDurableStateHydration<D extends Record<string, any>> {
|
|
2080
|
+
durableState: D;
|
|
2081
|
+
durableVersion: number;
|
|
2082
|
+
}
|
|
2078
2083
|
/**
|
|
2079
2084
|
* Metadata attached to wrapped actor task functions.
|
|
2080
2085
|
*/
|
|
@@ -2101,9 +2106,11 @@ declare class Actor<D extends Record<string, any> = AnyObject, R = AnyObject> {
|
|
|
2101
2106
|
readonly spec: ActorSpec<D, R>;
|
|
2102
2107
|
readonly kind: ActorKind;
|
|
2103
2108
|
private readonly sourceDefinition?;
|
|
2109
|
+
private readonly hydrateDurableState?;
|
|
2104
2110
|
private readonly stateByKey;
|
|
2105
2111
|
private readonly sessionByKey;
|
|
2106
2112
|
private readonly idempotencyByKey;
|
|
2113
|
+
private readonly pendingHydrationByKey;
|
|
2107
2114
|
private readonly writeQueueByKey;
|
|
2108
2115
|
private nextTaskBindingIndex;
|
|
2109
2116
|
/**
|
|
@@ -2154,7 +2161,10 @@ declare class Actor<D extends Record<string, any> = AnyObject, R = AnyObject> {
|
|
|
2154
2161
|
private resolveInitialDurableState;
|
|
2155
2162
|
private resolveInitialRuntimeState;
|
|
2156
2163
|
private ensureStateRecord;
|
|
2164
|
+
private maybeHydrateStateRecord;
|
|
2157
2165
|
private touchSession;
|
|
2166
|
+
private pruneExpiredActorKeys;
|
|
2167
|
+
private isSessionExpired;
|
|
2158
2168
|
private runWithOptionalIdempotency;
|
|
2159
2169
|
private runWithPerKeyWriteSerialization;
|
|
2160
2170
|
private getActiveIdempotencyRecord;
|
package/dist/index.js
CHANGED
|
@@ -3893,6 +3893,7 @@ var Actor = class {
|
|
|
3893
3893
|
this.stateByKey = /* @__PURE__ */ new Map();
|
|
3894
3894
|
this.sessionByKey = /* @__PURE__ */ new Map();
|
|
3895
3895
|
this.idempotencyByKey = /* @__PURE__ */ new Map();
|
|
3896
|
+
this.pendingHydrationByKey = /* @__PURE__ */ new Map();
|
|
3896
3897
|
this.writeQueueByKey = /* @__PURE__ */ new Map();
|
|
3897
3898
|
this.nextTaskBindingIndex = 0;
|
|
3898
3899
|
if (!spec.name || typeof spec.name !== "string") {
|
|
@@ -3904,6 +3905,7 @@ var Actor = class {
|
|
|
3904
3905
|
}
|
|
3905
3906
|
this.kind = options.isMeta || spec.kind === "meta" ? "meta" : "standard";
|
|
3906
3907
|
this.sourceDefinition = options.definitionSource;
|
|
3908
|
+
this.hydrateDurableState = options.hydrateDurableState;
|
|
3907
3909
|
this.spec = {
|
|
3908
3910
|
...spec,
|
|
3909
3911
|
defaultKey: normalizedDefaultKey,
|
|
@@ -3927,9 +3929,11 @@ var Actor = class {
|
|
|
3927
3929
|
bindingOptions.touchSession
|
|
3928
3930
|
);
|
|
3929
3931
|
const actorKey = this.resolveActorKey(normalizedInput, invocationOptions);
|
|
3930
|
-
|
|
3932
|
+
const now2 = Date.now();
|
|
3933
|
+
this.pruneExpiredActorKeys(now2);
|
|
3934
|
+
this.touchSession(actorKey, invocationOptions.touchSession, now2);
|
|
3931
3935
|
const runTask = async () => {
|
|
3932
|
-
const stateRecord = this.
|
|
3936
|
+
const stateRecord = await this.maybeHydrateStateRecord(actorKey);
|
|
3933
3937
|
stateRecord.lastReadAt = Date.now();
|
|
3934
3938
|
let durableStateChanged = false;
|
|
3935
3939
|
let runtimeStateChanged = false;
|
|
@@ -4098,6 +4102,7 @@ var Actor = class {
|
|
|
4098
4102
|
*/
|
|
4099
4103
|
getDurableState(actorKey) {
|
|
4100
4104
|
const key = normalizeActorKey(actorKey) ?? this.spec.defaultKey;
|
|
4105
|
+
this.pruneExpiredActorKeys(Date.now());
|
|
4101
4106
|
return cloneForDurableState(this.ensureStateRecord(key).durableState);
|
|
4102
4107
|
}
|
|
4103
4108
|
/**
|
|
@@ -4105,6 +4110,7 @@ var Actor = class {
|
|
|
4105
4110
|
*/
|
|
4106
4111
|
getRuntimeState(actorKey) {
|
|
4107
4112
|
const key = normalizeActorKey(actorKey) ?? this.spec.defaultKey;
|
|
4113
|
+
this.pruneExpiredActorKeys(Date.now());
|
|
4108
4114
|
return this.ensureStateRecord(key).runtimeState;
|
|
4109
4115
|
}
|
|
4110
4116
|
/**
|
|
@@ -4118,6 +4124,7 @@ var Actor = class {
|
|
|
4118
4124
|
*/
|
|
4119
4125
|
getDurableVersion(actorKey) {
|
|
4120
4126
|
const key = normalizeActorKey(actorKey) ?? this.spec.defaultKey;
|
|
4127
|
+
this.pruneExpiredActorKeys(Date.now());
|
|
4121
4128
|
return this.ensureStateRecord(key).version;
|
|
4122
4129
|
}
|
|
4123
4130
|
/**
|
|
@@ -4125,6 +4132,7 @@ var Actor = class {
|
|
|
4125
4132
|
*/
|
|
4126
4133
|
getRuntimeVersion(actorKey) {
|
|
4127
4134
|
const key = normalizeActorKey(actorKey) ?? this.spec.defaultKey;
|
|
4135
|
+
this.pruneExpiredActorKeys(Date.now());
|
|
4128
4136
|
return this.ensureStateRecord(key).runtimeVersion;
|
|
4129
4137
|
}
|
|
4130
4138
|
/**
|
|
@@ -4167,6 +4175,7 @@ var Actor = class {
|
|
|
4167
4175
|
this.stateByKey.clear();
|
|
4168
4176
|
this.sessionByKey.clear();
|
|
4169
4177
|
this.idempotencyByKey.clear();
|
|
4178
|
+
this.pendingHydrationByKey.clear();
|
|
4170
4179
|
if ((this.spec.loadPolicy ?? "eager") === "eager") {
|
|
4171
4180
|
this.ensureStateRecord(this.spec.defaultKey);
|
|
4172
4181
|
}
|
|
@@ -4178,6 +4187,7 @@ var Actor = class {
|
|
|
4178
4187
|
}
|
|
4179
4188
|
this.stateByKey.delete(normalizedKey);
|
|
4180
4189
|
this.sessionByKey.delete(normalizedKey);
|
|
4190
|
+
this.pendingHydrationByKey.delete(normalizedKey);
|
|
4181
4191
|
for (const key of this.idempotencyByKey.keys()) {
|
|
4182
4192
|
if (key.startsWith(`${normalizedKey}:`)) {
|
|
4183
4193
|
this.idempotencyByKey.delete(key);
|
|
@@ -4263,6 +4273,7 @@ var Actor = class {
|
|
|
4263
4273
|
runtimeState: this.resolveInitialRuntimeState(),
|
|
4264
4274
|
version: 0,
|
|
4265
4275
|
runtimeVersion: 0,
|
|
4276
|
+
hydrationResolved: this.hydrateDurableState === void 0,
|
|
4266
4277
|
createdAt: now2,
|
|
4267
4278
|
lastReadAt: now2,
|
|
4268
4279
|
lastDurableWriteAt: now2,
|
|
@@ -4272,6 +4283,54 @@ var Actor = class {
|
|
|
4272
4283
|
this.touchSession(actorKey, true, now2);
|
|
4273
4284
|
return record;
|
|
4274
4285
|
}
|
|
4286
|
+
async maybeHydrateStateRecord(actorKey) {
|
|
4287
|
+
const record = this.ensureStateRecord(actorKey);
|
|
4288
|
+
if (record.hydrationResolved || !this.hydrateDurableState) {
|
|
4289
|
+
return record;
|
|
4290
|
+
}
|
|
4291
|
+
const pending = this.pendingHydrationByKey.get(actorKey);
|
|
4292
|
+
if (pending) {
|
|
4293
|
+
return pending;
|
|
4294
|
+
}
|
|
4295
|
+
let hydrationPromise;
|
|
4296
|
+
hydrationPromise = (async () => {
|
|
4297
|
+
const current = this.ensureStateRecord(actorKey);
|
|
4298
|
+
if (current.hydrationResolved || !this.hydrateDurableState) {
|
|
4299
|
+
return current;
|
|
4300
|
+
}
|
|
4301
|
+
const snapshot = await this.hydrateDurableState(actorKey);
|
|
4302
|
+
const latest = this.ensureStateRecord(actorKey);
|
|
4303
|
+
if (latest.hydrationResolved) {
|
|
4304
|
+
return latest;
|
|
4305
|
+
}
|
|
4306
|
+
if (snapshot) {
|
|
4307
|
+
const durableVersion = Number(snapshot.durableVersion);
|
|
4308
|
+
if (!Number.isInteger(durableVersion) || durableVersion < 0) {
|
|
4309
|
+
throw new Error(
|
|
4310
|
+
`Actor "${this.spec.name}" received invalid hydrated durable version for key "${actorKey}"`
|
|
4311
|
+
);
|
|
4312
|
+
}
|
|
4313
|
+
if (!isObject2(snapshot.durableState) || Array.isArray(snapshot.durableState)) {
|
|
4314
|
+
throw new Error(
|
|
4315
|
+
`Actor "${this.spec.name}" received invalid hydrated durable state for key "${actorKey}"`
|
|
4316
|
+
);
|
|
4317
|
+
}
|
|
4318
|
+
const hydratedAt = Date.now();
|
|
4319
|
+
latest.durableState = cloneForDurableState(snapshot.durableState);
|
|
4320
|
+
latest.version = durableVersion;
|
|
4321
|
+
latest.lastReadAt = hydratedAt;
|
|
4322
|
+
latest.lastDurableWriteAt = hydratedAt;
|
|
4323
|
+
}
|
|
4324
|
+
latest.hydrationResolved = true;
|
|
4325
|
+
return latest;
|
|
4326
|
+
})().finally(() => {
|
|
4327
|
+
if (this.pendingHydrationByKey.get(actorKey) === hydrationPromise) {
|
|
4328
|
+
this.pendingHydrationByKey.delete(actorKey);
|
|
4329
|
+
}
|
|
4330
|
+
});
|
|
4331
|
+
this.pendingHydrationByKey.set(actorKey, hydrationPromise);
|
|
4332
|
+
return hydrationPromise;
|
|
4333
|
+
}
|
|
4275
4334
|
touchSession(actorKey, shouldTouch, touchedAt) {
|
|
4276
4335
|
if (!this.spec.session?.enabled || !shouldTouch) {
|
|
4277
4336
|
return;
|
|
@@ -4295,6 +4354,30 @@ var Actor = class {
|
|
|
4295
4354
|
existing.absoluteExpiresAt = touchedAt + absoluteTtlMs;
|
|
4296
4355
|
}
|
|
4297
4356
|
}
|
|
4357
|
+
pruneExpiredActorKeys(now2) {
|
|
4358
|
+
if (!this.spec.session?.enabled || this.sessionByKey.size === 0) {
|
|
4359
|
+
return;
|
|
4360
|
+
}
|
|
4361
|
+
for (const [actorKey, session] of this.sessionByKey.entries()) {
|
|
4362
|
+
if (!this.isSessionExpired(session, now2)) {
|
|
4363
|
+
continue;
|
|
4364
|
+
}
|
|
4365
|
+
if (this.writeQueueByKey.has(actorKey)) {
|
|
4366
|
+
continue;
|
|
4367
|
+
}
|
|
4368
|
+
this.stateByKey.delete(actorKey);
|
|
4369
|
+
this.sessionByKey.delete(actorKey);
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
isSessionExpired(session, now2) {
|
|
4373
|
+
if (session.absoluteExpiresAt !== null && now2 >= session.absoluteExpiresAt) {
|
|
4374
|
+
return true;
|
|
4375
|
+
}
|
|
4376
|
+
if (session.idleExpiresAt !== null && now2 >= session.idleExpiresAt) {
|
|
4377
|
+
return true;
|
|
4378
|
+
}
|
|
4379
|
+
return false;
|
|
4380
|
+
}
|
|
4298
4381
|
runWithOptionalIdempotency(taskBindingId, actorKey, options, runTask) {
|
|
4299
4382
|
const idempotencyPolicy = this.spec.idempotency;
|
|
4300
4383
|
const enabled = idempotencyPolicy?.enabled ?? false;
|
|
@@ -7345,7 +7428,8 @@ var Cadenza = class {
|
|
|
7345
7428
|
};
|
|
7346
7429
|
const actorOptions = {
|
|
7347
7430
|
isMeta: options.isMeta,
|
|
7348
|
-
definitionSource: definition
|
|
7431
|
+
definitionSource: definition,
|
|
7432
|
+
hydrateDurableState: options.hydrateDurableState
|
|
7349
7433
|
};
|
|
7350
7434
|
return this.createActor(spec, actorOptions);
|
|
7351
7435
|
}
|