@cadenza.io/core 3.26.1 → 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 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,6 +2161,7 @@ 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;
2158
2166
  private pruneExpiredActorKeys;
2159
2167
  private isSessionExpired;
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,6 +2161,7 @@ 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;
2158
2166
  private pruneExpiredActorKeys;
2159
2167
  private isSessionExpired;
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,
@@ -3931,7 +3933,7 @@ var Actor = class {
3931
3933
  this.pruneExpiredActorKeys(now2);
3932
3934
  this.touchSession(actorKey, invocationOptions.touchSession, now2);
3933
3935
  const runTask = async () => {
3934
- const stateRecord = this.ensureStateRecord(actorKey);
3936
+ const stateRecord = await this.maybeHydrateStateRecord(actorKey);
3935
3937
  stateRecord.lastReadAt = Date.now();
3936
3938
  let durableStateChanged = false;
3937
3939
  let runtimeStateChanged = false;
@@ -4173,6 +4175,7 @@ var Actor = class {
4173
4175
  this.stateByKey.clear();
4174
4176
  this.sessionByKey.clear();
4175
4177
  this.idempotencyByKey.clear();
4178
+ this.pendingHydrationByKey.clear();
4176
4179
  if ((this.spec.loadPolicy ?? "eager") === "eager") {
4177
4180
  this.ensureStateRecord(this.spec.defaultKey);
4178
4181
  }
@@ -4184,6 +4187,7 @@ var Actor = class {
4184
4187
  }
4185
4188
  this.stateByKey.delete(normalizedKey);
4186
4189
  this.sessionByKey.delete(normalizedKey);
4190
+ this.pendingHydrationByKey.delete(normalizedKey);
4187
4191
  for (const key of this.idempotencyByKey.keys()) {
4188
4192
  if (key.startsWith(`${normalizedKey}:`)) {
4189
4193
  this.idempotencyByKey.delete(key);
@@ -4269,6 +4273,7 @@ var Actor = class {
4269
4273
  runtimeState: this.resolveInitialRuntimeState(),
4270
4274
  version: 0,
4271
4275
  runtimeVersion: 0,
4276
+ hydrationResolved: this.hydrateDurableState === void 0,
4272
4277
  createdAt: now2,
4273
4278
  lastReadAt: now2,
4274
4279
  lastDurableWriteAt: now2,
@@ -4278,6 +4283,54 @@ var Actor = class {
4278
4283
  this.touchSession(actorKey, true, now2);
4279
4284
  return record;
4280
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
+ }
4281
4334
  touchSession(actorKey, shouldTouch, touchedAt) {
4282
4335
  if (!this.spec.session?.enabled || !shouldTouch) {
4283
4336
  return;
@@ -7375,7 +7428,8 @@ var Cadenza = class {
7375
7428
  };
7376
7429
  const actorOptions = {
7377
7430
  isMeta: options.isMeta,
7378
- definitionSource: definition
7431
+ definitionSource: definition,
7432
+ hydrateDurableState: options.hydrateDurableState
7379
7433
  };
7380
7434
  return this.createActor(spec, actorOptions);
7381
7435
  }