solve-engine 2.24.0 → 2.26.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/{BytecodeBuilder-aqVa7Plx.d.cts → BytecodeBuilder-DSWKZi4f.d.cts} +11 -9
- package/dist/{BytecodeBuilder-aqVa7Plx.d.ts → BytecodeBuilder-DSWKZi4f.d.ts} +11 -9
- package/dist/CalendarBackend-MdS3Bb4P.d.cts +181 -0
- package/dist/CalendarBackend-MdS3Bb4P.d.ts +181 -0
- package/dist/{Configuration-DnzYPmoK.d.cts → Configuration-BxriK9Kw.d.cts} +63 -4
- package/dist/{Configuration-DnzYPmoK.d.ts → Configuration-BxriK9Kw.d.ts} +63 -4
- package/dist/DateCalendar-B85t6Vk6.d.ts +90 -0
- package/dist/DateCalendar-CUGI1fT4.d.cts +90 -0
- package/dist/{EngineError-D1kXsjIj.d.cts → EngineError-C-kBwYlN.d.cts} +33 -4
- package/dist/{EngineError-D1kXsjIj.d.ts → EngineError-C-kBwYlN.d.ts} +33 -4
- package/dist/{FormattingSettings-bRKjU7wI.d.ts → FormattingSettings-ByO-4wiM.d.cts} +14 -0
- package/dist/{FormattingSettings-bRKjU7wI.d.cts → FormattingSettings-CvCzAfsz.d.ts} +14 -0
- package/dist/{Lexer-CqagTewQ.d.ts → Lexer-DhGhoAa2.d.cts} +96 -37
- package/dist/{Lexer-CCHFDcgP.d.cts → Lexer-DqtnZMC5.d.ts} +96 -37
- package/dist/{PackageCompatibility-CeAPVHZr.d.cts → PackageCompatibility-CNiTtC6S.d.cts} +1 -1
- package/dist/{PackageCompatibility-Bq82y0ZN.d.ts → PackageCompatibility-fj_f7uqC.d.ts} +1 -1
- package/dist/{PackageRegistry-B9-7fJGH.d.cts → PackageRegistry-BHBCFswo.d.cts} +377 -15
- package/dist/{PackageRegistry-CDEgfhT_.d.ts → PackageRegistry-CNZUwWHJ.d.ts} +377 -15
- package/dist/{Parselet-4oH5OuRt.d.cts → Parselet-DF3864la.d.cts} +26 -4
- package/dist/{Parselet-BwDqKFfK.d.ts → Parselet-Dgymjxzd.d.ts} +26 -4
- package/dist/{Token-CbP_OutD.d.cts → Token-D8f7yaz1.d.cts} +17 -0
- package/dist/{Token-CbP_OutD.d.ts → Token-D8f7yaz1.d.ts} +17 -0
- package/dist/{TokenNormalizer-OTPS0Otq.d.ts → TokenNormalizer-IF7nPRMp.d.ts} +46 -27
- package/dist/{TokenNormalizer-MXaKLJ_m.d.cts → TokenNormalizer-Pe3_390t.d.cts} +46 -27
- package/dist/{ScopeManager-BQBhlDAu.d.ts → VMBuiltins-CZiZRKn-.d.ts} +92 -10
- package/dist/{ScopeManager-DrXB3Nvm.d.cts → VMBuiltins-kv4rr0y5.d.cts} +92 -10
- package/dist/{VMCheckpoints-C5jC92o3.d.ts → VMCheckpoints-DXze6ypg.d.ts} +3 -3
- package/dist/{VMCheckpoints-BK32PTl2.d.cts → VMCheckpoints-yun4ocSv.d.cts} +3 -3
- package/dist/{Value-Ds3Gy07C.d.cts → Value-BYHw-x7q.d.cts} +57 -1
- package/dist/{Value-Ds3Gy07C.d.ts → Value-BYHw-x7q.d.ts} +57 -1
- package/dist/{WorkerError-DaNWQFp1.d.cts → WorkerError-BLZyNq6M.d.cts} +1 -1
- package/dist/{WorkerError-CVQbs6_D.d.ts → WorkerError-CfBJYueR.d.ts} +1 -1
- package/dist/chunk-2SYYKQ4Q.cjs +2 -0
- package/dist/chunk-2SYYKQ4Q.cjs.map +1 -0
- package/dist/chunk-6EYKVQT3.cjs +2 -0
- package/dist/chunk-6EYKVQT3.cjs.map +1 -0
- package/dist/chunk-7RC6RBS6.cjs +2 -0
- package/dist/chunk-7RC6RBS6.cjs.map +1 -0
- package/dist/chunk-AZX4NTYN.cjs +3 -0
- package/dist/chunk-AZX4NTYN.cjs.map +1 -0
- package/dist/{chunk-PTQCHMYA.js → chunk-BCR53EIL.js} +2 -2
- package/dist/{chunk-PTQCHMYA.js.map → chunk-BCR53EIL.js.map} +1 -1
- package/dist/chunk-BLJ6E577.js +2 -0
- package/dist/chunk-BLJ6E577.js.map +1 -0
- package/dist/{chunk-CMZSK6FY.cjs → chunk-C5GEMKUY.cjs} +3 -3
- package/dist/{chunk-CMZSK6FY.cjs.map → chunk-C5GEMKUY.cjs.map} +1 -1
- package/dist/chunk-CL7DM2FX.js +2 -0
- package/dist/chunk-CL7DM2FX.js.map +1 -0
- package/dist/chunk-CQLCTKMX.cjs +2 -0
- package/dist/chunk-CQLCTKMX.cjs.map +1 -0
- package/dist/chunk-CQY23OF5.cjs +5 -0
- package/dist/chunk-CQY23OF5.cjs.map +1 -0
- package/dist/chunk-D4VBWPWN.js +2 -0
- package/dist/chunk-D4VBWPWN.js.map +1 -0
- package/dist/chunk-E4HAKNBQ.cjs +2 -0
- package/dist/chunk-E4HAKNBQ.cjs.map +1 -0
- package/dist/{chunk-GP4H5OIT.js → chunk-ESZNJQ2C.js} +3 -3
- package/dist/{chunk-GP4H5OIT.js.map → chunk-ESZNJQ2C.js.map} +1 -1
- package/dist/{chunk-IJMNVBIS.js → chunk-FAO6DQ74.js} +2 -2
- package/dist/chunk-FAO6DQ74.js.map +1 -0
- package/dist/{chunk-2VE4OW4A.cjs → chunk-FDKTESBC.cjs} +2 -2
- package/dist/{chunk-2VE4OW4A.cjs.map → chunk-FDKTESBC.cjs.map} +1 -1
- package/dist/{chunk-RN3ISTM3.cjs → chunk-G2V33LFM.cjs} +2 -2
- package/dist/{chunk-RN3ISTM3.cjs.map → chunk-G2V33LFM.cjs.map} +1 -1
- package/dist/chunk-GJZIJK2Q.cjs +2 -0
- package/dist/chunk-GJZIJK2Q.cjs.map +1 -0
- package/dist/chunk-GQM6ICDM.js +2 -0
- package/dist/chunk-GQM6ICDM.js.map +1 -0
- package/dist/chunk-GVL3ZMS7.cjs +2 -0
- package/dist/chunk-GVL3ZMS7.cjs.map +1 -0
- package/dist/chunk-GXO7TSXQ.cjs +3 -0
- package/dist/chunk-GXO7TSXQ.cjs.map +1 -0
- package/dist/{chunk-OMHRBKAT.js → chunk-HANGBVEE.js} +2 -2
- package/dist/{chunk-OMHRBKAT.js.map → chunk-HANGBVEE.js.map} +1 -1
- package/dist/chunk-J4K72CQN.js +2 -0
- package/dist/chunk-J4K72CQN.js.map +1 -0
- package/dist/chunk-LE6WZLJ4.js +2 -0
- package/dist/chunk-LE6WZLJ4.js.map +1 -0
- package/dist/chunk-LMZDTQS5.js +3 -0
- package/dist/chunk-LMZDTQS5.js.map +1 -0
- package/dist/chunk-LQIRBP4Q.js +2 -0
- package/dist/chunk-LQIRBP4Q.js.map +1 -0
- package/dist/{chunk-UXR7JIPX.js → chunk-LTUYWJGO.js} +2 -2
- package/dist/{chunk-UXR7JIPX.js.map → chunk-LTUYWJGO.js.map} +1 -1
- package/dist/chunk-MCU7UYKG.js +3 -0
- package/dist/{chunk-7EP36NXE.js.map → chunk-MCU7UYKG.js.map} +1 -1
- package/dist/chunk-MTN53APQ.js +5 -0
- package/dist/chunk-MTN53APQ.js.map +1 -0
- package/dist/chunk-OPKJ2WWB.cjs +3 -0
- package/dist/chunk-OPKJ2WWB.cjs.map +1 -0
- package/dist/chunk-QQHHZPEW.cjs +2 -0
- package/dist/chunk-QQHHZPEW.cjs.map +1 -0
- package/dist/chunk-RRUGQ6DM.js +2 -0
- package/dist/chunk-RRUGQ6DM.js.map +1 -0
- package/dist/chunk-S3ODNMJS.js +2 -0
- package/dist/chunk-S3ODNMJS.js.map +1 -0
- package/dist/{chunk-CDNJZBM3.cjs → chunk-SE6ZCGZ5.cjs} +2 -2
- package/dist/{chunk-CDNJZBM3.cjs.map → chunk-SE6ZCGZ5.cjs.map} +1 -1
- package/dist/chunk-TC23XEAV.js +2 -0
- package/dist/chunk-TC23XEAV.js.map +1 -0
- package/dist/{chunk-MWWAKZOD.cjs → chunk-TMQ37JQY.cjs} +3 -3
- package/dist/{chunk-MWWAKZOD.cjs.map → chunk-TMQ37JQY.cjs.map} +1 -1
- package/dist/chunk-TPPM4QSS.cjs +2 -0
- package/dist/chunk-TPPM4QSS.cjs.map +1 -0
- package/dist/chunk-VB6QMU2W.js +3 -0
- package/dist/chunk-VB6QMU2W.js.map +1 -0
- package/dist/chunk-VQO54ZMA.js +2 -0
- package/dist/chunk-VQO54ZMA.js.map +1 -0
- package/dist/chunk-Y7XRA2EV.js +2 -0
- package/dist/chunk-Y7XRA2EV.js.map +1 -0
- package/dist/{chunk-5IJD5L3O.cjs → chunk-YBQOQTVL.cjs} +2 -2
- package/dist/chunk-YBQOQTVL.cjs.map +1 -0
- package/dist/chunk-YG7UWQFV.js +2 -0
- package/dist/chunk-YG7UWQFV.js.map +1 -0
- package/dist/chunk-YS2OW75C.cjs +2 -0
- package/dist/chunk-YS2OW75C.cjs.map +1 -0
- package/dist/chunk-ZC2NPRLO.js +3 -0
- package/dist/chunk-ZC2NPRLO.js.map +1 -0
- package/dist/chunk-ZDJTDFTR.cjs +2 -0
- package/dist/chunk-ZDJTDFTR.cjs.map +1 -0
- package/dist/chunk-ZE5KKMBV.cjs +2 -0
- package/dist/chunk-ZE5KKMBV.cjs.map +1 -0
- package/dist/chunk-ZSLVLMO5.cjs +2 -0
- package/dist/chunk-ZSLVLMO5.cjs.map +1 -0
- package/dist/constants.cjs +1 -1
- package/dist/constants.d.cts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/engine.cjs +1 -1
- package/dist/engine.d.cts +15 -13
- package/dist/engine.d.ts +15 -13
- package/dist/engine.js +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.d.cts +3 -3
- package/dist/errors.d.ts +3 -3
- package/dist/errors.js +1 -1
- package/dist/format.cjs +1 -1
- package/dist/format.d.cts +4 -3
- package/dist/format.d.ts +4 -3
- package/dist/format.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -14
- package/dist/index.d.ts +16 -14
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/language.d.cts +13 -12
- package/dist/language.d.ts +13 -12
- package/dist/lexer.cjs +1 -1
- package/dist/lexer.cjs.map +1 -1
- package/dist/lexer.d.cts +9 -8
- package/dist/lexer.d.ts +9 -8
- package/dist/lexer.js +1 -1
- package/dist/lexer.js.map +1 -1
- package/dist/normalizer.cjs +1 -1
- package/dist/normalizer.d.cts +3 -3
- package/dist/normalizer.d.ts +3 -3
- package/dist/normalizer.js +1 -1
- package/dist/packages.cjs +1 -1
- package/dist/packages.d.cts +12 -11
- package/dist/packages.d.ts +12 -11
- package/dist/packages.js +1 -1
- package/dist/parser.cjs +1 -1
- package/dist/parser.d.cts +6 -5
- package/dist/parser.d.ts +6 -5
- package/dist/parser.js +1 -1
- package/dist/{pipeline-69q2tsLE.d.cts → pipeline-BYkKNpql.d.cts} +1 -1
- package/dist/{pipeline-DTqGLPsV.d.ts → pipeline-Cl7KW8CB.d.ts} +1 -1
- package/dist/resolvers.d.cts +3 -3
- package/dist/resolvers.d.ts +3 -3
- package/dist/temporal.cjs +2 -0
- package/dist/temporal.cjs.map +1 -0
- package/dist/temporal.d.cts +201 -0
- package/dist/temporal.d.ts +201 -0
- package/dist/temporal.js +2 -0
- package/dist/temporal.js.map +1 -0
- package/dist/testing.cjs +2 -2
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +13 -12
- package/dist/testing.d.ts +13 -12
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/uom.cjs +1 -1
- package/dist/uom.d.cts +3 -3
- package/dist/uom.d.ts +3 -3
- package/dist/uom.js +1 -1
- package/dist/vm.cjs +1 -1
- package/dist/vm.cjs.map +1 -1
- package/dist/vm.d.cts +9 -57
- package/dist/vm.d.ts +9 -57
- package/dist/vm.js +1 -1
- package/dist/vm.js.map +1 -1
- package/dist/worker.cjs +2 -2
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +36 -14
- package/dist/worker.d.ts +36 -14
- package/dist/worker.js +2 -2
- package/dist/worker.js.map +1 -1
- package/package.json +16 -5
- package/dist/chunk-2BXNZM3G.js +0 -2
- package/dist/chunk-2BXNZM3G.js.map +0 -1
- package/dist/chunk-2JSNWYYA.cjs +0 -3
- package/dist/chunk-2JSNWYYA.cjs.map +0 -1
- package/dist/chunk-3SHWTGTP.js +0 -2
- package/dist/chunk-3SHWTGTP.js.map +0 -1
- package/dist/chunk-5GL4SAVH.cjs +0 -2
- package/dist/chunk-5GL4SAVH.cjs.map +0 -1
- package/dist/chunk-5IJD5L3O.cjs.map +0 -1
- package/dist/chunk-72N3ZRVE.cjs +0 -2
- package/dist/chunk-72N3ZRVE.cjs.map +0 -1
- package/dist/chunk-7EP36NXE.js +0 -3
- package/dist/chunk-A4JV7HRB.js +0 -3
- package/dist/chunk-A4JV7HRB.js.map +0 -1
- package/dist/chunk-AUOE7MFI.cjs +0 -2
- package/dist/chunk-AUOE7MFI.cjs.map +0 -1
- package/dist/chunk-BDF4VCQX.js +0 -3
- package/dist/chunk-BDF4VCQX.js.map +0 -1
- package/dist/chunk-D2Q2VFJ6.js +0 -2
- package/dist/chunk-D2Q2VFJ6.js.map +0 -1
- package/dist/chunk-DA7M6H63.cjs +0 -2
- package/dist/chunk-DA7M6H63.cjs.map +0 -1
- package/dist/chunk-ELQQKZN3.cjs +0 -3
- package/dist/chunk-ELQQKZN3.cjs.map +0 -1
- package/dist/chunk-F7QIBC4B.cjs +0 -3
- package/dist/chunk-F7QIBC4B.cjs.map +0 -1
- package/dist/chunk-HQ7BKXG7.js +0 -2
- package/dist/chunk-HQ7BKXG7.js.map +0 -1
- package/dist/chunk-IHAZL4EF.cjs +0 -2
- package/dist/chunk-IHAZL4EF.cjs.map +0 -1
- package/dist/chunk-IJMNVBIS.js.map +0 -1
- package/dist/chunk-IPSOWFCA.js +0 -2
- package/dist/chunk-IPSOWFCA.js.map +0 -1
- package/dist/chunk-LM6ZQUEU.js +0 -2
- package/dist/chunk-LM6ZQUEU.js.map +0 -1
- package/dist/chunk-LTJV3VJE.cjs +0 -2
- package/dist/chunk-LTJV3VJE.cjs.map +0 -1
- package/dist/chunk-NUF66H57.js +0 -5
- package/dist/chunk-NUF66H57.js.map +0 -1
- package/dist/chunk-PWWQZFAE.js +0 -2
- package/dist/chunk-PWWQZFAE.js.map +0 -1
- package/dist/chunk-RE6AIU6Y.js +0 -3
- package/dist/chunk-RE6AIU6Y.js.map +0 -1
- package/dist/chunk-RECRD45C.cjs +0 -5
- package/dist/chunk-RECRD45C.cjs.map +0 -1
- package/dist/chunk-RENO2AWO.js +0 -2
- package/dist/chunk-RENO2AWO.js.map +0 -1
- package/dist/chunk-RMCN5WFO.cjs +0 -2
- package/dist/chunk-RMCN5WFO.cjs.map +0 -1
- package/dist/chunk-S46R5QZP.cjs +0 -2
- package/dist/chunk-S46R5QZP.cjs.map +0 -1
- package/dist/chunk-UPH22K2T.cjs +0 -2
- package/dist/chunk-UPH22K2T.cjs.map +0 -1
- package/dist/chunk-UY3ID6JF.cjs +0 -2
- package/dist/chunk-UY3ID6JF.cjs.map +0 -1
- package/dist/chunk-WFOQRPA6.js +0 -2
- package/dist/chunk-WFOQRPA6.js.map +0 -1
- package/dist/chunk-XFJABR3B.js +0 -2
- package/dist/chunk-XFJABR3B.js.map +0 -1
|
@@ -199,6 +199,8 @@ declare class BytecodeBuilder {
|
|
|
199
199
|
private numbers;
|
|
200
200
|
private strings;
|
|
201
201
|
private stringIndex;
|
|
202
|
+
/** Value (or {@link NEGATIVE_ZERO_KEY}) to its slot in `numbers`, so a repeated literal is emitted once. */
|
|
203
|
+
private numberIndex;
|
|
202
204
|
private _hasAsync;
|
|
203
205
|
private userFunctionBodies;
|
|
204
206
|
private anonymousBodies;
|
|
@@ -222,17 +224,17 @@ declare class BytecodeBuilder {
|
|
|
222
224
|
/** Emit an {@link OpCode} instruction. */
|
|
223
225
|
emitOpcode(op: OpCode): void;
|
|
224
226
|
/**
|
|
225
|
-
* Emit a numeric literal:
|
|
226
|
-
*
|
|
227
|
-
* `PUSH_NUMBER <idx>`).
|
|
227
|
+
* Emit a numeric literal: interns `n` into the program's constant pool
|
|
228
|
+
* (deduplicated, like {@link emitString}) and writes its index into the
|
|
229
|
+
* opcode stream (read back by the VM as e.g. `PUSH_NUMBER <idx>`).
|
|
228
230
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
231
|
+
* Deduplicated so that the 256-entry pool counts distinct values rather
|
|
232
|
+
* than occurrences: a long line of repeated literals used to exhaust it
|
|
233
|
+
* long before it held 256 different numbers. Negative zero keeps its own
|
|
234
|
+
* slot, because `1 / -0` is not `1 / 0`; NaN shares one, since every NaN
|
|
235
|
+
* reads the same.
|
|
234
236
|
*
|
|
235
|
-
* @throws If the
|
|
237
|
+
* @throws If the pool would exceed 256 distinct entries.
|
|
236
238
|
*/
|
|
237
239
|
emitNumber(n: number): void;
|
|
238
240
|
/**
|
|
@@ -199,6 +199,8 @@ declare class BytecodeBuilder {
|
|
|
199
199
|
private numbers;
|
|
200
200
|
private strings;
|
|
201
201
|
private stringIndex;
|
|
202
|
+
/** Value (or {@link NEGATIVE_ZERO_KEY}) to its slot in `numbers`, so a repeated literal is emitted once. */
|
|
203
|
+
private numberIndex;
|
|
202
204
|
private _hasAsync;
|
|
203
205
|
private userFunctionBodies;
|
|
204
206
|
private anonymousBodies;
|
|
@@ -222,17 +224,17 @@ declare class BytecodeBuilder {
|
|
|
222
224
|
/** Emit an {@link OpCode} instruction. */
|
|
223
225
|
emitOpcode(op: OpCode): void;
|
|
224
226
|
/**
|
|
225
|
-
* Emit a numeric literal:
|
|
226
|
-
*
|
|
227
|
-
* `PUSH_NUMBER <idx>`).
|
|
227
|
+
* Emit a numeric literal: interns `n` into the program's constant pool
|
|
228
|
+
* (deduplicated, like {@link emitString}) and writes its index into the
|
|
229
|
+
* opcode stream (read back by the VM as e.g. `PUSH_NUMBER <idx>`).
|
|
228
230
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
231
|
+
* Deduplicated so that the 256-entry pool counts distinct values rather
|
|
232
|
+
* than occurrences: a long line of repeated literals used to exhaust it
|
|
233
|
+
* long before it held 256 different numbers. Negative zero keeps its own
|
|
234
|
+
* slot, because `1 / -0` is not `1 / 0`; NaN shares one, since every NaN
|
|
235
|
+
* reads the same.
|
|
234
236
|
*
|
|
235
|
-
* @throws If the
|
|
237
|
+
* @throws If the pool would exceed 256 distinct entries.
|
|
236
238
|
*/
|
|
237
239
|
emitNumber(n: number): void;
|
|
238
240
|
/**
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The calendar the engine computes dates with, behind one interface.
|
|
3
|
+
*
|
|
4
|
+
* Every date the engine holds is an instant: epoch milliseconds in a
|
|
5
|
+
* `Datetime` value, with no zone attached. Every date *question* the engine
|
|
6
|
+
* answers is a calendar question about that instant: which day it falls on,
|
|
7
|
+
* what the same wall-clock time a month later is, whether it is a Saturday,
|
|
8
|
+
* how it is written out. Answering those needs a time zone and a set of
|
|
9
|
+
* calendar rules, and until this interface existed the answer was always the
|
|
10
|
+
* JavaScript `Date` object read in the host process's own zone, scattered
|
|
11
|
+
* across some twenty files.
|
|
12
|
+
*
|
|
13
|
+
* A backend gathers those questions into one place so that a different
|
|
14
|
+
* implementation can answer them. Two ship with the engine. {@link DateCalendar}
|
|
15
|
+
* is the same `Date` code moved behind these methods and is the default, so
|
|
16
|
+
* nothing observable changes for a host that configures nothing. The
|
|
17
|
+
* `Temporal` backend (`solve-engine/temporal`) answers the same questions
|
|
18
|
+
* through a `Temporal` implementation the host hands it, native or polyfilled,
|
|
19
|
+
* and carries a time zone of its own rather than the process's; the engine
|
|
20
|
+
* never imports a polyfill.
|
|
21
|
+
*
|
|
22
|
+
* The contract is deliberately narrow. Methods take and return plain numbers
|
|
23
|
+
* and strings, never a `Date` or a `Temporal` object, so a `Datetime` value's
|
|
24
|
+
* payload stays a number and no worker message, snapshot or arena entry
|
|
25
|
+
* changes shape. Arithmetic whose answer does not depend on a zone (a day
|
|
26
|
+
* number, the days in a month, the ISO week) lives beside the backend in
|
|
27
|
+
* `calendar/Gregorian.ts` rather than behind it, because sharing one
|
|
28
|
+
* implementation is what keeps two backends from disagreeing on it.
|
|
29
|
+
*
|
|
30
|
+
* The shape is public and settled by the two backends that implement it. A
|
|
31
|
+
* host may implement the interface directly; a change to it follows the
|
|
32
|
+
* package's semantic versioning, so a new required method is a major.
|
|
33
|
+
*
|
|
34
|
+
* @module CalendarBackend
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* The calendar fields of one instant, read in the backend's zone.
|
|
38
|
+
*
|
|
39
|
+
* `month0` counts from zero (January is 0) and `weekday` from Sunday (0) to
|
|
40
|
+
* Saturday (6), matching `Date` at every internal boundary so a backend cannot
|
|
41
|
+
* be off by one against the code that reads it. Every field is `NaN` for an
|
|
42
|
+
* instant the backend cannot represent.
|
|
43
|
+
*/
|
|
44
|
+
interface CalendarFields {
|
|
45
|
+
readonly year: number;
|
|
46
|
+
/** Zero-based month: 0 is January, 11 is December. */
|
|
47
|
+
readonly month0: number;
|
|
48
|
+
/** Day of the month, from 1. */
|
|
49
|
+
readonly day: number;
|
|
50
|
+
/** Day of the week: 0 is Sunday, 6 is Saturday. */
|
|
51
|
+
readonly weekday: number;
|
|
52
|
+
readonly hour: number;
|
|
53
|
+
readonly minute: number;
|
|
54
|
+
readonly second: number;
|
|
55
|
+
readonly millisecond: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The calendar fields a named time zone shows for an instant: the date and
|
|
59
|
+
* the wall-clock time, with the same zero-based month as {@link CalendarFields}.
|
|
60
|
+
* Sub-second precision is never needed for a zone conversion, so there is no
|
|
61
|
+
* millisecond field.
|
|
62
|
+
*/
|
|
63
|
+
interface ZonedFields {
|
|
64
|
+
readonly year: number;
|
|
65
|
+
/** Zero-based month: 0 is January, 11 is December. */
|
|
66
|
+
readonly month0: number;
|
|
67
|
+
/** Day of the month, from 1. */
|
|
68
|
+
readonly day: number;
|
|
69
|
+
readonly hour: number;
|
|
70
|
+
readonly minute: number;
|
|
71
|
+
readonly second: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The calendar computations the engine performs, as one replaceable unit.
|
|
75
|
+
*
|
|
76
|
+
* "Local" throughout means the backend's own zone: the host process's zone for
|
|
77
|
+
* the `Date` backend, the configured zone for the `Temporal` one. A local
|
|
78
|
+
* operation that cannot be represented (an instant past the range the backend
|
|
79
|
+
* supports, a field that is not finite) answers `NaN`, never throws; the
|
|
80
|
+
* caller decides what an unrepresentable date means for its form.
|
|
81
|
+
*
|
|
82
|
+
* The four named-zone methods are the exception, and the contract is stated on
|
|
83
|
+
* each: a zone name the runtime does not know throws its `RangeError`, as
|
|
84
|
+
* `Intl` and `Temporal` both do, because a zone reaches the backend only from
|
|
85
|
+
* the engine's own zone registry and an unknown one is a data fault the caller
|
|
86
|
+
* should see rather than a `NaN` to display. Callers hand those methods a
|
|
87
|
+
* finite instant (`now()`, or a literal already checked).
|
|
88
|
+
*/
|
|
89
|
+
interface CalendarBackend {
|
|
90
|
+
/** The current instant, in epoch milliseconds. Read at evaluation time, never baked into bytecode. */
|
|
91
|
+
now(): number;
|
|
92
|
+
/** The local calendar fields of an instant, `weekday` included. See {@link CalendarFields}. */
|
|
93
|
+
fields(epochMs: number): CalendarFields;
|
|
94
|
+
/**
|
|
95
|
+
* Local midnight on a calendar date, in epoch milliseconds.
|
|
96
|
+
*
|
|
97
|
+
* Fields overflow the way `Date`'s do (month 12 is January of the next
|
|
98
|
+
* year, day 0 the last day of the month before), which is what a caller
|
|
99
|
+
* checking for a rolled-over literal relies on: build the date, read its
|
|
100
|
+
* fields back, and a 30 February shows up as 1 or 2 March.
|
|
101
|
+
*/
|
|
102
|
+
localMidnight(year: number, month0: number, day: number): number;
|
|
103
|
+
/**
|
|
104
|
+
* A wall-clock time on a calendar date, given as minutes past local
|
|
105
|
+
* midnight, in epoch milliseconds.
|
|
106
|
+
*
|
|
107
|
+
* The minutes name a clock reading, not an elapsed span: 540 minutes is
|
|
108
|
+
* 09:00 on that date even on a day with a daylight-saving transition,
|
|
109
|
+
* where 540 minutes of elapsed time from midnight would land at 10:00 or
|
|
110
|
+
* 08:00.
|
|
111
|
+
*/
|
|
112
|
+
localWallClock(year: number, month0: number, day: number, minutesPastMidnight: number): number;
|
|
113
|
+
/**
|
|
114
|
+
* Move an instant by whole calendar days, holding the local wall-clock
|
|
115
|
+
* time. A day that contains a daylight-saving transition is 23 or 25
|
|
116
|
+
* hours long, so this is a field step, not an addition of milliseconds.
|
|
117
|
+
*/
|
|
118
|
+
addDays(epochMs: number, days: number): number;
|
|
119
|
+
/**
|
|
120
|
+
* Move an instant by whole calendar months, holding the local wall-clock
|
|
121
|
+
* time and clamping the day to the length of the month landed in: 31
|
|
122
|
+
* January plus a month is 28 February, or 29 in a leap year, never 3 March.
|
|
123
|
+
*/
|
|
124
|
+
addMonths(epochMs: number, months: number): number;
|
|
125
|
+
/** The local zone's offset from UTC at an instant, in minutes, positive when ahead of UTC. */
|
|
126
|
+
utcOffsetMinutes(epochMs: number): number;
|
|
127
|
+
/**
|
|
128
|
+
* Parse an ISO 8601 date or date-time string to epoch milliseconds, or
|
|
129
|
+
* `NaN` when it names no instant.
|
|
130
|
+
*
|
|
131
|
+
* A date-only string (`2019-04-01`) is UTC midnight; a date-time with no
|
|
132
|
+
* offset and no `Z` is local time. Both readings are the ECMAScript ones,
|
|
133
|
+
* and every backend reproduces them so the two spellings of a literal keep
|
|
134
|
+
* meaning the same instant whichever backend is in use.
|
|
135
|
+
*/
|
|
136
|
+
parseIso8601(text: string): number;
|
|
137
|
+
/** The spelled-out local date in a locale, `Tuesday, March 10, 2026` in `en`. */
|
|
138
|
+
formatLongDate(epochMs: number, locale: string): string;
|
|
139
|
+
/** The local time of day in a locale, `9:30:00 AM` in `en`. */
|
|
140
|
+
formatTimeOfDay(epochMs: number, locale: string): string;
|
|
141
|
+
/**
|
|
142
|
+
* A named IANA zone's offset from UTC at an instant, in minutes, positive
|
|
143
|
+
* when ahead of UTC. Throws the runtime's `RangeError` for a zone it does
|
|
144
|
+
* not know or an instant it cannot represent.
|
|
145
|
+
*/
|
|
146
|
+
zoneOffsetMinutes(zone: string, epochMs: number): number;
|
|
147
|
+
/**
|
|
148
|
+
* The calendar date and wall-clock time a named IANA zone shows for an
|
|
149
|
+
* instant. Throws the runtime's `RangeError` for a zone it does not know or
|
|
150
|
+
* an instant it cannot represent.
|
|
151
|
+
*/
|
|
152
|
+
fieldsInZone(zone: string, epochMs: number): ZonedFields;
|
|
153
|
+
/**
|
|
154
|
+
* The wall-clock time in a named IANA zone, `1:00 AM`, in the `en-US` style
|
|
155
|
+
* the timezone forms answer in. Throws the runtime's `RangeError` for a
|
|
156
|
+
* zone it does not know or an instant it cannot represent.
|
|
157
|
+
*/
|
|
158
|
+
formatTimeInZone(zone: string, epochMs: number): string;
|
|
159
|
+
/**
|
|
160
|
+
* The calendar date in a named IANA zone, `July 31, 2026`, in the `en-US`
|
|
161
|
+
* style the timezone forms answer in. Throws the runtime's `RangeError` for
|
|
162
|
+
* a zone it does not know or an instant it cannot represent.
|
|
163
|
+
*/
|
|
164
|
+
formatDateInZone(zone: string, epochMs: number): string;
|
|
165
|
+
/**
|
|
166
|
+
* The zone this backend's "local" means, when it can name one.
|
|
167
|
+
*
|
|
168
|
+
* Optional, and deliberately so: the interface's own contract says a new
|
|
169
|
+
* REQUIRED method is a major, and every backend written against the shipped
|
|
170
|
+
* shape has to keep compiling. A backend that reads the host process's zone
|
|
171
|
+
* (the default {@link DateCalendar}) leaves it undefined rather than
|
|
172
|
+
* reporting a zone it does not itself compute in; one built for a named zone
|
|
173
|
+
* (`dateCalendarInZone`, the `Temporal` backend) returns that IANA name.
|
|
174
|
+
*
|
|
175
|
+
* A caller that needs "which zone is local here" and gets `undefined` should
|
|
176
|
+
* fall back to whatever it meant by local before, never guess a name.
|
|
177
|
+
*/
|
|
178
|
+
zone?(): string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export type { CalendarBackend as C, ZonedFields as Z, CalendarFields as a };
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The calendar the engine computes dates with, behind one interface.
|
|
3
|
+
*
|
|
4
|
+
* Every date the engine holds is an instant: epoch milliseconds in a
|
|
5
|
+
* `Datetime` value, with no zone attached. Every date *question* the engine
|
|
6
|
+
* answers is a calendar question about that instant: which day it falls on,
|
|
7
|
+
* what the same wall-clock time a month later is, whether it is a Saturday,
|
|
8
|
+
* how it is written out. Answering those needs a time zone and a set of
|
|
9
|
+
* calendar rules, and until this interface existed the answer was always the
|
|
10
|
+
* JavaScript `Date` object read in the host process's own zone, scattered
|
|
11
|
+
* across some twenty files.
|
|
12
|
+
*
|
|
13
|
+
* A backend gathers those questions into one place so that a different
|
|
14
|
+
* implementation can answer them. Two ship with the engine. {@link DateCalendar}
|
|
15
|
+
* is the same `Date` code moved behind these methods and is the default, so
|
|
16
|
+
* nothing observable changes for a host that configures nothing. The
|
|
17
|
+
* `Temporal` backend (`solve-engine/temporal`) answers the same questions
|
|
18
|
+
* through a `Temporal` implementation the host hands it, native or polyfilled,
|
|
19
|
+
* and carries a time zone of its own rather than the process's; the engine
|
|
20
|
+
* never imports a polyfill.
|
|
21
|
+
*
|
|
22
|
+
* The contract is deliberately narrow. Methods take and return plain numbers
|
|
23
|
+
* and strings, never a `Date` or a `Temporal` object, so a `Datetime` value's
|
|
24
|
+
* payload stays a number and no worker message, snapshot or arena entry
|
|
25
|
+
* changes shape. Arithmetic whose answer does not depend on a zone (a day
|
|
26
|
+
* number, the days in a month, the ISO week) lives beside the backend in
|
|
27
|
+
* `calendar/Gregorian.ts` rather than behind it, because sharing one
|
|
28
|
+
* implementation is what keeps two backends from disagreeing on it.
|
|
29
|
+
*
|
|
30
|
+
* The shape is public and settled by the two backends that implement it. A
|
|
31
|
+
* host may implement the interface directly; a change to it follows the
|
|
32
|
+
* package's semantic versioning, so a new required method is a major.
|
|
33
|
+
*
|
|
34
|
+
* @module CalendarBackend
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* The calendar fields of one instant, read in the backend's zone.
|
|
38
|
+
*
|
|
39
|
+
* `month0` counts from zero (January is 0) and `weekday` from Sunday (0) to
|
|
40
|
+
* Saturday (6), matching `Date` at every internal boundary so a backend cannot
|
|
41
|
+
* be off by one against the code that reads it. Every field is `NaN` for an
|
|
42
|
+
* instant the backend cannot represent.
|
|
43
|
+
*/
|
|
44
|
+
interface CalendarFields {
|
|
45
|
+
readonly year: number;
|
|
46
|
+
/** Zero-based month: 0 is January, 11 is December. */
|
|
47
|
+
readonly month0: number;
|
|
48
|
+
/** Day of the month, from 1. */
|
|
49
|
+
readonly day: number;
|
|
50
|
+
/** Day of the week: 0 is Sunday, 6 is Saturday. */
|
|
51
|
+
readonly weekday: number;
|
|
52
|
+
readonly hour: number;
|
|
53
|
+
readonly minute: number;
|
|
54
|
+
readonly second: number;
|
|
55
|
+
readonly millisecond: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The calendar fields a named time zone shows for an instant: the date and
|
|
59
|
+
* the wall-clock time, with the same zero-based month as {@link CalendarFields}.
|
|
60
|
+
* Sub-second precision is never needed for a zone conversion, so there is no
|
|
61
|
+
* millisecond field.
|
|
62
|
+
*/
|
|
63
|
+
interface ZonedFields {
|
|
64
|
+
readonly year: number;
|
|
65
|
+
/** Zero-based month: 0 is January, 11 is December. */
|
|
66
|
+
readonly month0: number;
|
|
67
|
+
/** Day of the month, from 1. */
|
|
68
|
+
readonly day: number;
|
|
69
|
+
readonly hour: number;
|
|
70
|
+
readonly minute: number;
|
|
71
|
+
readonly second: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The calendar computations the engine performs, as one replaceable unit.
|
|
75
|
+
*
|
|
76
|
+
* "Local" throughout means the backend's own zone: the host process's zone for
|
|
77
|
+
* the `Date` backend, the configured zone for the `Temporal` one. A local
|
|
78
|
+
* operation that cannot be represented (an instant past the range the backend
|
|
79
|
+
* supports, a field that is not finite) answers `NaN`, never throws; the
|
|
80
|
+
* caller decides what an unrepresentable date means for its form.
|
|
81
|
+
*
|
|
82
|
+
* The four named-zone methods are the exception, and the contract is stated on
|
|
83
|
+
* each: a zone name the runtime does not know throws its `RangeError`, as
|
|
84
|
+
* `Intl` and `Temporal` both do, because a zone reaches the backend only from
|
|
85
|
+
* the engine's own zone registry and an unknown one is a data fault the caller
|
|
86
|
+
* should see rather than a `NaN` to display. Callers hand those methods a
|
|
87
|
+
* finite instant (`now()`, or a literal already checked).
|
|
88
|
+
*/
|
|
89
|
+
interface CalendarBackend {
|
|
90
|
+
/** The current instant, in epoch milliseconds. Read at evaluation time, never baked into bytecode. */
|
|
91
|
+
now(): number;
|
|
92
|
+
/** The local calendar fields of an instant, `weekday` included. See {@link CalendarFields}. */
|
|
93
|
+
fields(epochMs: number): CalendarFields;
|
|
94
|
+
/**
|
|
95
|
+
* Local midnight on a calendar date, in epoch milliseconds.
|
|
96
|
+
*
|
|
97
|
+
* Fields overflow the way `Date`'s do (month 12 is January of the next
|
|
98
|
+
* year, day 0 the last day of the month before), which is what a caller
|
|
99
|
+
* checking for a rolled-over literal relies on: build the date, read its
|
|
100
|
+
* fields back, and a 30 February shows up as 1 or 2 March.
|
|
101
|
+
*/
|
|
102
|
+
localMidnight(year: number, month0: number, day: number): number;
|
|
103
|
+
/**
|
|
104
|
+
* A wall-clock time on a calendar date, given as minutes past local
|
|
105
|
+
* midnight, in epoch milliseconds.
|
|
106
|
+
*
|
|
107
|
+
* The minutes name a clock reading, not an elapsed span: 540 minutes is
|
|
108
|
+
* 09:00 on that date even on a day with a daylight-saving transition,
|
|
109
|
+
* where 540 minutes of elapsed time from midnight would land at 10:00 or
|
|
110
|
+
* 08:00.
|
|
111
|
+
*/
|
|
112
|
+
localWallClock(year: number, month0: number, day: number, minutesPastMidnight: number): number;
|
|
113
|
+
/**
|
|
114
|
+
* Move an instant by whole calendar days, holding the local wall-clock
|
|
115
|
+
* time. A day that contains a daylight-saving transition is 23 or 25
|
|
116
|
+
* hours long, so this is a field step, not an addition of milliseconds.
|
|
117
|
+
*/
|
|
118
|
+
addDays(epochMs: number, days: number): number;
|
|
119
|
+
/**
|
|
120
|
+
* Move an instant by whole calendar months, holding the local wall-clock
|
|
121
|
+
* time and clamping the day to the length of the month landed in: 31
|
|
122
|
+
* January plus a month is 28 February, or 29 in a leap year, never 3 March.
|
|
123
|
+
*/
|
|
124
|
+
addMonths(epochMs: number, months: number): number;
|
|
125
|
+
/** The local zone's offset from UTC at an instant, in minutes, positive when ahead of UTC. */
|
|
126
|
+
utcOffsetMinutes(epochMs: number): number;
|
|
127
|
+
/**
|
|
128
|
+
* Parse an ISO 8601 date or date-time string to epoch milliseconds, or
|
|
129
|
+
* `NaN` when it names no instant.
|
|
130
|
+
*
|
|
131
|
+
* A date-only string (`2019-04-01`) is UTC midnight; a date-time with no
|
|
132
|
+
* offset and no `Z` is local time. Both readings are the ECMAScript ones,
|
|
133
|
+
* and every backend reproduces them so the two spellings of a literal keep
|
|
134
|
+
* meaning the same instant whichever backend is in use.
|
|
135
|
+
*/
|
|
136
|
+
parseIso8601(text: string): number;
|
|
137
|
+
/** The spelled-out local date in a locale, `Tuesday, March 10, 2026` in `en`. */
|
|
138
|
+
formatLongDate(epochMs: number, locale: string): string;
|
|
139
|
+
/** The local time of day in a locale, `9:30:00 AM` in `en`. */
|
|
140
|
+
formatTimeOfDay(epochMs: number, locale: string): string;
|
|
141
|
+
/**
|
|
142
|
+
* A named IANA zone's offset from UTC at an instant, in minutes, positive
|
|
143
|
+
* when ahead of UTC. Throws the runtime's `RangeError` for a zone it does
|
|
144
|
+
* not know or an instant it cannot represent.
|
|
145
|
+
*/
|
|
146
|
+
zoneOffsetMinutes(zone: string, epochMs: number): number;
|
|
147
|
+
/**
|
|
148
|
+
* The calendar date and wall-clock time a named IANA zone shows for an
|
|
149
|
+
* instant. Throws the runtime's `RangeError` for a zone it does not know or
|
|
150
|
+
* an instant it cannot represent.
|
|
151
|
+
*/
|
|
152
|
+
fieldsInZone(zone: string, epochMs: number): ZonedFields;
|
|
153
|
+
/**
|
|
154
|
+
* The wall-clock time in a named IANA zone, `1:00 AM`, in the `en-US` style
|
|
155
|
+
* the timezone forms answer in. Throws the runtime's `RangeError` for a
|
|
156
|
+
* zone it does not know or an instant it cannot represent.
|
|
157
|
+
*/
|
|
158
|
+
formatTimeInZone(zone: string, epochMs: number): string;
|
|
159
|
+
/**
|
|
160
|
+
* The calendar date in a named IANA zone, `July 31, 2026`, in the `en-US`
|
|
161
|
+
* style the timezone forms answer in. Throws the runtime's `RangeError` for
|
|
162
|
+
* a zone it does not know or an instant it cannot represent.
|
|
163
|
+
*/
|
|
164
|
+
formatDateInZone(zone: string, epochMs: number): string;
|
|
165
|
+
/**
|
|
166
|
+
* The zone this backend's "local" means, when it can name one.
|
|
167
|
+
*
|
|
168
|
+
* Optional, and deliberately so: the interface's own contract says a new
|
|
169
|
+
* REQUIRED method is a major, and every backend written against the shipped
|
|
170
|
+
* shape has to keep compiling. A backend that reads the host process's zone
|
|
171
|
+
* (the default {@link DateCalendar}) leaves it undefined rather than
|
|
172
|
+
* reporting a zone it does not itself compute in; one built for a named zone
|
|
173
|
+
* (`dateCalendarInZone`, the `Temporal` backend) returns that IANA name.
|
|
174
|
+
*
|
|
175
|
+
* A caller that needs "which zone is local here" and gets `undefined` should
|
|
176
|
+
* fall back to whatever it meant by local before, never guess a name.
|
|
177
|
+
*/
|
|
178
|
+
zone?(): string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export type { CalendarBackend as C, ZonedFields as Z, CalendarFields as a };
|
|
@@ -58,11 +58,25 @@ type HolidayCalendar = HolidayPredicate | Iterable<string | number | Date>;
|
|
|
58
58
|
* a host can match its readers' locale. `'MDY'` is what lets a US reader's
|
|
59
59
|
* `12/25/2023` parse, which `'auto'` refuses because the slash defaults to
|
|
60
60
|
* day-first.
|
|
61
|
+
* - `'locale'`: the order the reader's own machine writes dates in, asked of
|
|
62
|
+
* `Intl` once per engine (see `calendar/HostLocale.ts`). Where that cannot
|
|
63
|
+
* be answered, the engine reads dates exactly as `'auto'` does and reports
|
|
64
|
+
* the fact through `ExpressionEngine.getDateReading()`, rather than guessing.
|
|
65
|
+
* A host that is not the reader (a server rendering someone else's document)
|
|
66
|
+
* names the reader instead, through {@link DateConfig.inputLocale}.
|
|
61
67
|
*
|
|
62
|
-
* Only
|
|
63
|
-
*
|
|
68
|
+
* Only ambiguous literals are affected. A spelled-out month (`March 9, 2024`)
|
|
69
|
+
* is never ambiguous; nor is a hyphen literal with a four-digit leading group
|
|
70
|
+
* (`2024-03-09`), which has no reading but year-month-day and is read as ISO
|
|
71
|
+
* under every order, timestamp or not.
|
|
64
72
|
*/
|
|
65
|
-
type DateInputOrder = 'auto' | 'DMY' | 'MDY' | 'YMD';
|
|
73
|
+
type DateInputOrder = 'auto' | 'locale' | 'DMY' | 'MDY' | 'YMD';
|
|
74
|
+
/**
|
|
75
|
+
* What a date-shaped numeric run the configured order cannot read does: report
|
|
76
|
+
* the problem, or fall through to the arithmetic it is spelled like. See
|
|
77
|
+
* {@link DateConfig.onAmbiguous}.
|
|
78
|
+
*/
|
|
79
|
+
type DateAmbiguity = 'refuse' | 'arithmetic';
|
|
66
80
|
/** Date-related engine configuration: offset limits, input order, holidays. */
|
|
67
81
|
interface DateConfig {
|
|
68
82
|
/**
|
|
@@ -70,6 +84,51 @@ interface DateConfig {
|
|
|
70
84
|
* `'auto'` (by separator, the historic behaviour). See {@link DateInputOrder}.
|
|
71
85
|
*/
|
|
72
86
|
readonly inputOrder: DateInputOrder;
|
|
87
|
+
/**
|
|
88
|
+
* The BCP-47 tag `'locale'` reads the order from, `"en-US"` or `"de-DE"`.
|
|
89
|
+
* Unset, the host machine is asked.
|
|
90
|
+
*
|
|
91
|
+
* Read ONLY when {@link inputOrder} is `'locale'`. Setting it beside any
|
|
92
|
+
* other order changes no result: a field that quietly switched inference on
|
|
93
|
+
* would make the predictable mistake a wrong reading rather than no change.
|
|
94
|
+
* Its shape is checked wherever it is set, though, and a tag `Intl` refuses
|
|
95
|
+
* (`"en_US"`, with an underscore) raises `DATE_INPUT_LOCALE_INVALID` at
|
|
96
|
+
* construction, because a locale silently ignored is a date order silently
|
|
97
|
+
* wrong.
|
|
98
|
+
*
|
|
99
|
+
* Deliberately separate from the engine's `locale` option, which is a
|
|
100
|
+
* language (`'en' | 'de' | 'fr'`) and carries no region. Day/month order is a
|
|
101
|
+
* region question: a bare `en` probes as month-first while a UK machine
|
|
102
|
+
* resolves to `en-GB` and probes as day-first, so wiring the two together
|
|
103
|
+
* would flip every existing British engine to month-first.
|
|
104
|
+
*/
|
|
105
|
+
readonly inputLocale?: string;
|
|
106
|
+
/**
|
|
107
|
+
* What a date-shaped numeric run the resolved order cannot read does.
|
|
108
|
+
* Defaults to `'refuse'`.
|
|
109
|
+
*
|
|
110
|
+
* - `'refuse'`: the line reports a structured Error value naming the
|
|
111
|
+
* problem, DATE_ORDER_MISMATCH when another order would have read it and
|
|
112
|
+
* DATE_NOT_A_CALENDAR_DAY when no order names a real day. `12/25/2026` on
|
|
113
|
+
* a day-first engine says there is no month 25, rather than answering
|
|
114
|
+
* 0.00.
|
|
115
|
+
* - `'arithmetic'`: the run falls through to the division or subtraction it
|
|
116
|
+
* is spelled like, which is what every version before this one did.
|
|
117
|
+
* `12/25/2026` is 0.00 again, and `2026-02-29` is 1,995.
|
|
118
|
+
*
|
|
119
|
+
* The refusal is scoped to runs nobody writes as arithmetic: a two-step
|
|
120
|
+
* chain ending in a four-digit denominator (`03/04/2026` as division is
|
|
121
|
+
* 0.0004), and an ISO-shaped hyphen run. A four-digit LEADING group is
|
|
122
|
+
* ordinary arithmetic (`1000/10/5` is 20, `1024/8/2` is 64) and is never
|
|
123
|
+
* refused, and neither is a run whose groups are all one or two digits
|
|
124
|
+
* (`12/13/14` stays 0.07), because a two-digit year is too weak a signal to
|
|
125
|
+
* hang a refusal on.
|
|
126
|
+
*
|
|
127
|
+
* The one refusal this setting does not restore is the dot form
|
|
128
|
+
* (`25.12.2026` under a month-first order): its only other outcome is a
|
|
129
|
+
* parse error, and an error is not an answer.
|
|
130
|
+
*/
|
|
131
|
+
readonly onAmbiguous: DateAmbiguity;
|
|
73
132
|
/**
|
|
74
133
|
* How far forward a date offset whose COST grows with the offset may reach,
|
|
75
134
|
* in years. Enforced by `vm/VM.ts`'s `addBusinessDays()`.
|
|
@@ -439,4 +498,4 @@ interface ValidationResult {
|
|
|
439
498
|
warnings?: string[];
|
|
440
499
|
}
|
|
441
500
|
|
|
442
|
-
export { ConfigManager as C,
|
|
501
|
+
export { ConfigManager as C, type DateAmbiguity as D, type EngineConfigOverride as E, type HolidayCalendar as H, type PerformanceConfig as P, type VMConfig as V, type WorkerConfig as W, type EngineConfig as a, DEFAULT_CONFIG as b, type DateConfig as c, type DiagnosticConfig as d, type HolidayPredicate as e, type ValidationConfig as f, type ValidationResult as g };
|
|
@@ -58,11 +58,25 @@ type HolidayCalendar = HolidayPredicate | Iterable<string | number | Date>;
|
|
|
58
58
|
* a host can match its readers' locale. `'MDY'` is what lets a US reader's
|
|
59
59
|
* `12/25/2023` parse, which `'auto'` refuses because the slash defaults to
|
|
60
60
|
* day-first.
|
|
61
|
+
* - `'locale'`: the order the reader's own machine writes dates in, asked of
|
|
62
|
+
* `Intl` once per engine (see `calendar/HostLocale.ts`). Where that cannot
|
|
63
|
+
* be answered, the engine reads dates exactly as `'auto'` does and reports
|
|
64
|
+
* the fact through `ExpressionEngine.getDateReading()`, rather than guessing.
|
|
65
|
+
* A host that is not the reader (a server rendering someone else's document)
|
|
66
|
+
* names the reader instead, through {@link DateConfig.inputLocale}.
|
|
61
67
|
*
|
|
62
|
-
* Only
|
|
63
|
-
*
|
|
68
|
+
* Only ambiguous literals are affected. A spelled-out month (`March 9, 2024`)
|
|
69
|
+
* is never ambiguous; nor is a hyphen literal with a four-digit leading group
|
|
70
|
+
* (`2024-03-09`), which has no reading but year-month-day and is read as ISO
|
|
71
|
+
* under every order, timestamp or not.
|
|
64
72
|
*/
|
|
65
|
-
type DateInputOrder = 'auto' | 'DMY' | 'MDY' | 'YMD';
|
|
73
|
+
type DateInputOrder = 'auto' | 'locale' | 'DMY' | 'MDY' | 'YMD';
|
|
74
|
+
/**
|
|
75
|
+
* What a date-shaped numeric run the configured order cannot read does: report
|
|
76
|
+
* the problem, or fall through to the arithmetic it is spelled like. See
|
|
77
|
+
* {@link DateConfig.onAmbiguous}.
|
|
78
|
+
*/
|
|
79
|
+
type DateAmbiguity = 'refuse' | 'arithmetic';
|
|
66
80
|
/** Date-related engine configuration: offset limits, input order, holidays. */
|
|
67
81
|
interface DateConfig {
|
|
68
82
|
/**
|
|
@@ -70,6 +84,51 @@ interface DateConfig {
|
|
|
70
84
|
* `'auto'` (by separator, the historic behaviour). See {@link DateInputOrder}.
|
|
71
85
|
*/
|
|
72
86
|
readonly inputOrder: DateInputOrder;
|
|
87
|
+
/**
|
|
88
|
+
* The BCP-47 tag `'locale'` reads the order from, `"en-US"` or `"de-DE"`.
|
|
89
|
+
* Unset, the host machine is asked.
|
|
90
|
+
*
|
|
91
|
+
* Read ONLY when {@link inputOrder} is `'locale'`. Setting it beside any
|
|
92
|
+
* other order changes no result: a field that quietly switched inference on
|
|
93
|
+
* would make the predictable mistake a wrong reading rather than no change.
|
|
94
|
+
* Its shape is checked wherever it is set, though, and a tag `Intl` refuses
|
|
95
|
+
* (`"en_US"`, with an underscore) raises `DATE_INPUT_LOCALE_INVALID` at
|
|
96
|
+
* construction, because a locale silently ignored is a date order silently
|
|
97
|
+
* wrong.
|
|
98
|
+
*
|
|
99
|
+
* Deliberately separate from the engine's `locale` option, which is a
|
|
100
|
+
* language (`'en' | 'de' | 'fr'`) and carries no region. Day/month order is a
|
|
101
|
+
* region question: a bare `en` probes as month-first while a UK machine
|
|
102
|
+
* resolves to `en-GB` and probes as day-first, so wiring the two together
|
|
103
|
+
* would flip every existing British engine to month-first.
|
|
104
|
+
*/
|
|
105
|
+
readonly inputLocale?: string;
|
|
106
|
+
/**
|
|
107
|
+
* What a date-shaped numeric run the resolved order cannot read does.
|
|
108
|
+
* Defaults to `'refuse'`.
|
|
109
|
+
*
|
|
110
|
+
* - `'refuse'`: the line reports a structured Error value naming the
|
|
111
|
+
* problem, DATE_ORDER_MISMATCH when another order would have read it and
|
|
112
|
+
* DATE_NOT_A_CALENDAR_DAY when no order names a real day. `12/25/2026` on
|
|
113
|
+
* a day-first engine says there is no month 25, rather than answering
|
|
114
|
+
* 0.00.
|
|
115
|
+
* - `'arithmetic'`: the run falls through to the division or subtraction it
|
|
116
|
+
* is spelled like, which is what every version before this one did.
|
|
117
|
+
* `12/25/2026` is 0.00 again, and `2026-02-29` is 1,995.
|
|
118
|
+
*
|
|
119
|
+
* The refusal is scoped to runs nobody writes as arithmetic: a two-step
|
|
120
|
+
* chain ending in a four-digit denominator (`03/04/2026` as division is
|
|
121
|
+
* 0.0004), and an ISO-shaped hyphen run. A four-digit LEADING group is
|
|
122
|
+
* ordinary arithmetic (`1000/10/5` is 20, `1024/8/2` is 64) and is never
|
|
123
|
+
* refused, and neither is a run whose groups are all one or two digits
|
|
124
|
+
* (`12/13/14` stays 0.07), because a two-digit year is too weak a signal to
|
|
125
|
+
* hang a refusal on.
|
|
126
|
+
*
|
|
127
|
+
* The one refusal this setting does not restore is the dot form
|
|
128
|
+
* (`25.12.2026` under a month-first order): its only other outcome is a
|
|
129
|
+
* parse error, and an error is not an answer.
|
|
130
|
+
*/
|
|
131
|
+
readonly onAmbiguous: DateAmbiguity;
|
|
73
132
|
/**
|
|
74
133
|
* How far forward a date offset whose COST grows with the offset may reach,
|
|
75
134
|
* in years. Enforced by `vm/VM.ts`'s `addBusinessDays()`.
|
|
@@ -439,4 +498,4 @@ interface ValidationResult {
|
|
|
439
498
|
warnings?: string[];
|
|
440
499
|
}
|
|
441
500
|
|
|
442
|
-
export { ConfigManager as C,
|
|
501
|
+
export { ConfigManager as C, type DateAmbiguity as D, type EngineConfigOverride as E, type HolidayCalendar as H, type PerformanceConfig as P, type VMConfig as V, type WorkerConfig as W, type EngineConfig as a, DEFAULT_CONFIG as b, type DateConfig as c, type DiagnosticConfig as d, type HolidayPredicate as e, type ValidationConfig as f, type ValidationResult as g };
|