#中介軟體概覽
中介軟體為排程器加入排程策略與應用程式所需的欄位。模型計算記憶狀態與間隔,chrono 預設把這些間隔轉換為到期時間;中介軟體位於兩者之間,可以調整目標保留率或間隔、管理學習步驟,以及維護模型未曾見過的額外卡片資料。各部分的職責見整體架構。
排程器由模型、chrono 與中介軟體組合而成。讓模型只負責記憶狀態更新與間隔計算,讓 chrono 負責時間值與天數運算;中介軟體在核心操作之前提供輸入、在其後調整結果,並承載這兩者未定義的欄位。
複製這段提示詞,讓 AI Agent 遵守 Middleware 契約,避免混入排程器內部實作或持久化邏輯。
#定義欄位與行為
Middleware 定義包含必填的 name,以及選填的 schema、預設值和處理函式。defineMiddleware() 根據 schema 推導處理函式中的型別。可以從 ts-fsrs 匯入;內建策略也可從 ts-fsrs/middlewares 匯入。
| 定義 | 用途 |
|---|---|
schema.config | 驗證傳給 create({ config }) 的設定。中介軟體共用排程器的設定物件。 |
schema.card | 宣告並驗證輸入、輸出卡片上的欄位。 |
schema.revlog | 宣告並驗證複習紀錄欄位,包括復原所需的舊值。 |
schema.cardInitInput | 宣告 newCard() 接受的額外輸入,例如卡片 ID。 |
scheduleStatus | 註冊自訂排程狀態,例如 scheduleStatus: ['buried'],讓組合後的型別與結果驗證接受這些狀態。 |
handlers.nextInterval | 在 next() 前調整 ctx.candidate.desiredRetention[ctx.input.grade],或在其後調整 ctx.scheduledDays。複習與直接呼叫 scheduler.nextInterval() 時都會執行。 |
defaultValue.card | 在建立或重設卡片時提供欄位值,透過 ctx.operation 區分這兩種操作。 |
handlers.review / handlers.rollback | 在呼叫下一個處理函式前後,更新 ctx.result。執行方式見生命週期。 |
Schema 同時提供執行時驗證與 TypeScript 型別推導。以下使用實作了 Standard Schema 的 Zod。在自己的應用程式中執行範例時,除了 ts-fsrs 之外還需要安裝它:
npm install zodyarn add zodpnpm add zodbun add zoddeno add npm:zod#加入支援復原的連續答對計數
範例中,除了 Again 以外的評分都會讓 streak 加一,最多累積到 streakLimit;Again 會將它歸零。複習處理函式把原計數存入 revlog,因此即使歸零後復原,也能還原先前的值:
import { const dateChrono: Chrono<{
readonly time: SRSSchema<{
input: Date;
output: Date;
}>;
readonly config: SRSSchema<{
input: {
readonly fractionalDays?: boolean | undefined;
};
output: {
readonly fractionalDays: boolean;
};
}>;
readonly fields: {
readonly card: SRSSchema<{
input: {
dueAt: Date;
lastReviewAt?: (Date | null) | undefined;
};
output: {
dueAt: Date;
lastReviewAt: Date | null;
};
}>;
readonly revlog: SRSSchema<{
input: {
dueAt: Date;
lastReviewAt?: (Date | null) | undefined;
reviewTime: Date;
};
output: {
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
};
}>;
};
}>
dateChrono , function defineMiddleware<const Name extends PropertyKey, const Schema extends MiddlewareDefinitionSchema = EmptyPart, const Status extends string = never>(definition: MiddlewareDefinition<Schema, Name, Status>): Middleware<Name, Prettify<Assign<MiddlewareStatusEnv<Status>, Schema>> extends MiddlewareEnv ? MiddlewareEnv & Prettify<Assign<MiddlewareStatusEnv<Status>, Schema>> : never> defineMiddleware , function defineScheduler<const M extends AnyModel, const C extends AnyChrono>(definition: {
readonly model: M;
readonly chrono: C;
}): CustomScheduler<SchedulerNameOf<M>, SchedulerEnvFor<M, C, readonly []>, M, C>
defineScheduler , type Rating = 0 | 1 | 2 | 3 | 4
const Rating: Readonly<{
readonly Manual: 0;
readonly Again: 1;
readonly Hard: 2;
readonly Good: 3;
readonly Easy: 4;
}>
Rating } from 'ts-fsrs'
import { const FSRS6_DEFAULT_WEIGHTS: number[] FSRS6_DEFAULT_WEIGHTS , const FSRS6Model: Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>
FSRS6Model } from 'ts-fsrs/models/fsrs-6'
import { z } from 'zod'
const streakFields = z.object({ streak: z.int().nonnegative() })
const const streakMiddleware: Middleware<symbol, {
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}>
streakMiddleware = defineMiddleware<symbol, {
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}, never>(definition: MiddlewareDefinition<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}, symbol, never>): Middleware<...>
defineMiddleware ({
name: Symbol('guide.streak'),
schema: {
config: z.object({ streakLimit: z.int().positive() }),
card: streakFields,
revlog: streakFields,
},
defaultValue?: {
readonly card?: FieldDefault<z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>, MiddlewareDefaultValueContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}>> | undefined;
readonly revlog?: FieldDefault<z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>, MiddlewareDefaultValueContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}>> | undefined;
} | undefined
defaultValue : { card?: FieldDefault<z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>, MiddlewareDefaultValueContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}>> | undefined
card : () => ({ streak: 0 }) },
handlers?: {
readonly nextInterval?: NextIntervalMiddlewareHandler<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> | undefined;
readonly review?: MiddlewareHandler<ReviewMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<...>> | undefined;
readonly rollback?: MiddlewareHandler<...> | undefined;
} | undefined
handlers : {
review?: MiddlewareHandler<ReviewMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card", "card" | "revlog">> | undefined
review (ctx: ReviewMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card", "card" | "revlog">
ctx , next) {
next()
const { card, grade } = ctx: ReviewMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card", "card" | "revlog">
ctx .input: {
readonly card: {
streak: number;
} & SchedulerCoreFields<string>;
readonly grade: Grade;
readonly desiredRetention?: number;
} & {
readonly now: unknown;
} & {
readonly card: {
readonly scheduleStatus: "new" | "learning" | "review";
};
}
input
ctx: ReviewMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card", "card" | "revlog">
ctx .result: ReviewMiddlewareResult<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & {
readonly card: {
scheduleStatus?: "new" | "learning" | "review" | undefined;
};
readonly revlog: {
scheduleStatus?: "new" | "learning" | "review" | undefined;
};
}
result .revlog: Partial<Mutable<{
streak: number;
} & SchedulerCoreFields<string> & {
readonly rating: Grade;
}>> & Record<string, unknown> & {
scheduleStatus?: "new" | "learning" | "review" | undefined;
}
revlog .streak = card.streak
ctx: ReviewMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card", "card" | "revlog">
ctx .result: ReviewMiddlewareResult<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & {
readonly card: {
scheduleStatus?: "new" | "learning" | "review" | undefined;
};
readonly revlog: {
scheduleStatus?: "new" | "learning" | "review" | undefined;
};
}
result .card: Partial<Mutable<{
streak: number;
} & SchedulerCoreFields<string>>> & Record<string, unknown> & {
scheduleStatus?: "new" | "learning" | "review" | undefined;
}
card .streak =
grade === const Rating: Readonly<{
readonly Manual: 0;
readonly Again: 1;
readonly Hard: 2;
readonly Good: 3;
readonly Easy: 4;
}>
Rating .Again
? 0
: Math.min(card.streak + 1, ctx: ReviewMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card", "card" | "revlog">
ctx .MiddlewareContextBase<{ readonly config: ZodObject<{ streakLimit: ZodInt; }, $strip>; readonly card: ZodObject<{ streak: ZodInt; }, $strip>; readonly revlog: ZodObject<...>; }>.config: MiddlewareContextConfig<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}>
config .streakLimit)
},
rollback?: MiddlewareHandler<RollbackMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card" | "revlog", "card">> | undefined
rollback (ctx: RollbackMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card" | "revlog", "card">
ctx , next) {
next()
ctx: RollbackMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card" | "revlog", "card">
ctx .result: RollbackMiddlewareResult<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & {
readonly card: {
scheduleStatus?: "new" | "learning" | "review" | undefined;
};
}
result .card: Partial<Mutable<{
streak: number;
} & SchedulerCoreFields<string>>> & Record<string, unknown> & {
scheduleStatus?: "new" | "learning" | "review" | undefined;
}
card .streak = ctx: RollbackMiddlewareContext<{
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}> & MiddlewareStatusContext<never, "card" | "revlog", "card">
ctx .input: {
readonly card: {
streak: number;
} & SchedulerCoreFields<string>;
readonly revlog: {
streak: number;
} & SchedulerCoreFields<string> & {
readonly rating: Grade;
};
} & {
readonly card: {
readonly scheduleStatus: "new" | "learning" | "review";
};
readonly revlog: {
readonly scheduleStatus: "new" | "learning" | "review";
};
}
input .revlog: {
streak: number;
} & SchedulerCoreFields<string> & {
readonly rating: Grade;
} & {
readonly scheduleStatus: "new" | "learning" | "review";
}
revlog .streak
},
},
})
const const base: CustomScheduler<"fsrs-6", SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, Model<...>, Chrono<...>>
base = defineScheduler<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>>(definition: {
...;
}): CustomScheduler<...>
defineScheduler ({ model: Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>
model : const FSRS6Model: Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>
FSRS6Model , chrono: Chrono<{
readonly time: SRSSchema<{
input: Date;
output: Date;
}>;
readonly config: SRSSchema<{
input: {
readonly fractionalDays?: boolean | undefined;
};
output: {
readonly fractionalDays: boolean;
};
}>;
readonly fields: {
readonly card: SRSSchema<{
input: {
dueAt: Date;
lastReviewAt?: (Date | null) | undefined;
};
output: {
dueAt: Date;
lastReviewAt: Date | null;
};
}>;
readonly revlog: SRSSchema<{
input: {
dueAt: Date;
lastReviewAt?: (Date | null) | undefined;
reviewTime: Date;
};
output: {
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
};
}>;
};
}>
chrono : const dateChrono: Chrono<{
readonly time: SRSSchema<{
input: Date;
output: Date;
}>;
readonly config: SRSSchema<{
input: {
readonly fractionalDays?: boolean | undefined;
};
output: {
readonly fractionalDays: boolean;
};
}>;
readonly fields: {
readonly card: SRSSchema<{
input: {
dueAt: Date;
lastReviewAt?: (Date | null) | undefined;
};
output: {
dueAt: Date;
lastReviewAt: Date | null;
};
}>;
readonly revlog: SRSSchema<{
input: {
dueAt: Date;
lastReviewAt?: (Date | null) | undefined;
reviewTime: Date;
};
output: {
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
};
}>;
};
}>
dateChrono })
const const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler = const base: CustomScheduler<"fsrs-6", SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, Model<...>, Chrono<...>>
base .CustomScheduler<"fsrs-6", SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, Model<...>, Chrono<...>>.use: <readonly [Middleware<symbol, {
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}>]>(middlewares_0: Middleware<symbol, {
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}>) => CustomScheduler<...>
Returns a new scheduler definition extended with the supplied middleware.
The current scheduler and any cores created from it remain unchanged.
use (const streakMiddleware: Middleware<symbol, {
readonly config: z.ZodObject<{
streakLimit: z.ZodInt;
}, z.core.$strip>;
readonly card: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
readonly revlog: z.ZodObject<{
streak: z.ZodInt;
}, z.core.$strip>;
}>
streakMiddleware ).CustomScheduler<"fsrs-6", ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>.create: (ctx: {
readonly config: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
} & {
readonly fractionalDays?: boolean | undefined;
} & EmptyPart & {
streakLimit: number;
};
}) => SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
create ({
config: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
} & {
readonly fractionalDays?: boolean | undefined;
} & EmptyPart & {
streakLimit: number;
}
config : {
weights: const FSRS6_DEFAULT_WEIGHTS: number[] FSRS6_DEFAULT_WEIGHTS ,
enableShortTerm: false,
numRelearningSteps: 0,
streakLimit: 2,
},
})
const now = new Date('2026-01-01T00:00:00.000Z')
const const initial: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
initial = const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler .SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.newCard: (options?: {
readonly now?: Date | undefined;
} | undefined) => {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
newCard ({ now })
const const good: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
good = const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler .SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.review: (input: {
readonly card: Readonly<Omit<Readonly<Omit<Readonly<{
stability: number;
difficulty: number;
} & {
dueAt: Date;
lastReviewAt: Date | null;
} & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
streak: number;
}, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>;
readonly grade: Grade;
readonly now?: Date | undefined;
}) => {
...;
}
review ({ card: const initial: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
initial , grade: const Rating: Readonly<{
readonly Manual: 0;
readonly Again: 1;
readonly Hard: 2;
readonly Good: 3;
readonly Easy: 4;
}>
Rating .Good, now })
const const again: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
again = const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler .SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.review: (input: {
readonly card: Readonly<Omit<Readonly<Omit<Readonly<{
stability: number;
difficulty: number;
} & {
dueAt: Date;
lastReviewAt: Date | null;
} & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
streak: number;
}, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>;
readonly grade: Grade;
readonly now?: Date | undefined;
}) => {
...;
}
review ({
card: const good: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
good .card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
card ,
grade: const Rating: Readonly<{
readonly Manual: 0;
readonly Again: 1;
readonly Hard: 2;
readonly Good: 3;
readonly Easy: 4;
}>
Rating .Again,
now: const good: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
good .card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
card .dueAt,
})
const const restored: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
restored = const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler .SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.rollback: (input: {
readonly card: Mutable<Omit<Mutable<Omit<Mutable<{
stability: number;
difficulty: number;
} & {
dueAt: Date;
lastReviewAt: Date | null;
} & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
streak: number;
}, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>;
readonly revlog: Mutable<...>;
}) => {
...;
}
rollback (const again: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
again )
const const preview: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
readonly grade: Grade;
}[]
preview = Array.ArrayConstructor.from<{
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
readonly grade: Grade;
}>(iterable: Iterable<{
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
readonly grade: Grade;
}> | ArrayLike<...>): {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
readonly grade: Grade;
}[] (+3 overloads)
Creates an array from an iterable object.
@paramiterable An iterable object to convert to an array. from (
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
readonly name: "fsrs-6";
readonly config: SRSSchema<{
input: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
output: {
readonly weights: number[];
readonly enableShortTerm: boolean;
readonly numRelearningSteps: number;
};
}>;
readonly memoryState: SRSSchema<{
input: {
stability: number;
difficulty: number;
};
output: {
stability: number;
difficulty: number;
};
}>;
readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler .SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.preview: (input: {
readonly card: Readonly<Omit<Readonly<Omit<Readonly<{
stability: number;
difficulty: number;
} & {
dueAt: Date;
lastReviewAt: Date | null;
} & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
streak: number;
}, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>;
readonly now?: Date | undefined;
}) => PreviewResult<...>
preview ({ card: const restored: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
restored , now: const good: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
good .card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
card .dueAt })
)
console.log(
JSON.stringify(
{
initial: const initial: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
initial .streak,
afterGood: const good: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
good .card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
card .streak,
afterAgain: const again: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
again .card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
card .streak,
savedBeforeAgain: const again: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
}
again .revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
}
revlog .streak,
restored: const restored: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
restored .streak,
preview: const preview: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
readonly grade: Grade;
}[]
preview .Array<{ card: { streak: number; stability: number; difficulty: number; dueAt: Date; lastReviewAt: Date | null; state: State; scheduleStatus: "new" | "learning" | "review"; }; revlog: { ...; }; readonly grade: Grade; }>.map<number>(callbackfn: (value: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
readonly grade: Grade;
}, index: number, array: {
card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
};
revlog: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
reviewTime: Date;
state: State;
scheduleStatus: "new" | "learning" | "review";
rating: Grade;
};
readonly grade: Grade;
}[]) => number, thisArg?: any): number[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. map (({ card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
card }) => card: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
card .streak),
afterPreview: const restored: {
streak: number;
stability: number;
difficulty: number;
dueAt: Date;
lastReviewAt: Date | null;
state: State;
scheduleStatus: "new" | "learning" | "review";
}
restored .streak,
},
null,
2
)
)
計數從 0 增加到 1,評為 Again 後回到 0,復原後還原為 1。預覽中,Again、Hard、Good、Easy 對應的計數分別是 0、2、2、2,輸入卡片的計數仍為 1。
base.use(streakMiddleware) 回傳新的排程器定義,原本的 base 不變。需要保留回傳值,或像範例一樣接著呼叫 .create()。由於中介軟體透過 use 組合,處理函式會以固定順序環繞排程器核心——詳見生命週期。