mirror of
https://github.com/docmost/docmost.git
synced 2026-06-10 01:52:43 +08:00
feat(base-formula): add date and coercion functions, wire registry
This commit is contained in:
@@ -45,8 +45,7 @@ describe("evaluate", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: unskip after Task 15 wires the function registry with round/concat.
|
||||
describe.skip("evaluate with registered functions", () => {
|
||||
describe("evaluate with registered functions", () => {
|
||||
it("invokes registered functions", () => {
|
||||
expect(run('round(1.6)', {})).toBe(2);
|
||||
expect(run('concat("a", "b", "c")', {})).toBe("abc");
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// TODO: unskip after Task 15 lands date functions and populates the registry.
|
||||
import { parseRaw, resolve, typecheck, registry } from "@docmost/base-formula/server";
|
||||
import type { FormulaAST } from "@docmost/base-formula/server";
|
||||
|
||||
@@ -9,7 +8,7 @@ const mk = (src: string, propTypes: Record<string, "number" | "string" | "boolea
|
||||
return { ast: resolved.ast, typeMap };
|
||||
};
|
||||
|
||||
describe.skip("typecheck", () => {
|
||||
describe("typecheck", () => {
|
||||
it("infers number for arithmetic", () => {
|
||||
const { ast, typeMap } = mk('prop("Price") * 2', { Price: "number" });
|
||||
expect(typecheck(ast, typeMap, registry).resultType).toBe("number");
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// packages/base-formula/src/functions/date.ts
|
||||
import { register } from "./registry";
|
||||
import { makeErrorCell } from "../error";
|
||||
|
||||
const toDate = (v: unknown): Date | null => {
|
||||
if (v == null) return null;
|
||||
const d = new Date(String(v));
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
};
|
||||
|
||||
register({
|
||||
name: "now", arity: { min: 0, max: 0 }, paramTypes: [], returnType: "date",
|
||||
eval: () => new Date().toISOString(),
|
||||
doc: "Current timestamp.", category: "date",
|
||||
});
|
||||
register({
|
||||
name: "today", arity: { min: 0, max: 0 }, paramTypes: [], returnType: "date",
|
||||
eval: () => {
|
||||
const d = new Date(); d.setUTCHours(0, 0, 0, 0); return d.toISOString();
|
||||
},
|
||||
doc: "Midnight UTC of today.", category: "date",
|
||||
});
|
||||
register({
|
||||
name: "dateAdd", arity: { min: 3, max: 3 }, paramTypes: ["date", "number", "string"], returnType: "date",
|
||||
eval: ([base, amt, unit]) => {
|
||||
const d = toDate(base);
|
||||
if (!d) return makeErrorCell("DATE_INVALID", "invalid date");
|
||||
const n = Number(amt);
|
||||
const u = String(unit);
|
||||
const r = new Date(d);
|
||||
if (u === "days") r.setUTCDate(r.getUTCDate() + n);
|
||||
else if (u === "hours") r.setUTCHours(r.getUTCHours() + n);
|
||||
else if (u === "minutes") r.setUTCMinutes(r.getUTCMinutes() + n);
|
||||
else if (u === "months") r.setUTCMonth(r.getUTCMonth() + n);
|
||||
else if (u === "years") r.setUTCFullYear(r.getUTCFullYear() + n);
|
||||
else return makeErrorCell("TYPE_MISMATCH", `unknown unit ${u}`);
|
||||
return r.toISOString();
|
||||
},
|
||||
doc: "Adds a duration to a date. Units: days, hours, minutes, months, years.", category: "date",
|
||||
});
|
||||
register({
|
||||
name: "dateBetween", arity: { min: 3, max: 3 }, paramTypes: ["date", "date", "string"], returnType: "number",
|
||||
eval: ([a, b, unit]) => {
|
||||
const da = toDate(a), db = toDate(b);
|
||||
if (!da || !db) return makeErrorCell("DATE_INVALID", "invalid date");
|
||||
const ms = db.getTime() - da.getTime();
|
||||
const u = String(unit);
|
||||
if (u === "days") return Math.floor(ms / 86_400_000);
|
||||
if (u === "hours") return Math.floor(ms / 3_600_000);
|
||||
if (u === "minutes") return Math.floor(ms / 60_000);
|
||||
return makeErrorCell("TYPE_MISMATCH", `unknown unit ${u}`);
|
||||
},
|
||||
doc: "Difference between two dates in a given unit.", category: "date",
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
// packages/base-formula/src/functions/index.ts
|
||||
import "./logic";
|
||||
import "./math";
|
||||
import "./string";
|
||||
import "./date";
|
||||
import "./coercion";
|
||||
export { registry, register } from "./registry";
|
||||
export type { FormulaFn } from "./registry";
|
||||
@@ -7,7 +7,8 @@ export * from "./parser";
|
||||
export * from "./resolver";
|
||||
export * from "./typecheck";
|
||||
export * from "./format";
|
||||
export { registry, register } from "./functions/registry";
|
||||
export type { FormulaFn } from "./functions/registry";
|
||||
import "./functions/index"; // side-effect: populate registry
|
||||
export { registry, register } from "./functions/index";
|
||||
export type { FormulaFn } from "./functions/index";
|
||||
export * from "./graph";
|
||||
export * from "./eval";
|
||||
|
||||
Reference in New Issue
Block a user