@lakutata/entrypoint 3.0.0-beta.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/LICENSE +23 -0
- package/README.md +183 -0
- package/THIRD_PARTY_NOTICES.md +206 -0
- package/dist/cjs/Entrypoint.d.ts +868 -0
- package/dist/cjs/Entrypoint.js +998 -0
- package/dist/cjs/adapter/commander/CommanderCLIEntrypoint.d.ts +192 -0
- package/dist/cjs/adapter/commander/CommanderCLIEntrypoint.js +439 -0
- package/dist/cjs/adapter/commander/Prompt.d.ts +22 -0
- package/dist/cjs/adapter/commander/Prompt.js +164 -0
- package/dist/cjs/adapter/commander/Width.d.ts +12 -0
- package/dist/cjs/adapter/commander/Width.js +67 -0
- package/dist/cjs/adapter/electron/ElectronIPCServiceEntrypoint.d.ts +203 -0
- package/dist/cjs/adapter/electron/ElectronIPCServiceEntrypoint.js +249 -0
- package/dist/cjs/adapter/express/ExpressHTTPEntrypoint.d.ts +172 -0
- package/dist/cjs/adapter/express/ExpressHTTPEntrypoint.js +301 -0
- package/dist/cjs/adapter/express/Responders.d.ts +89 -0
- package/dist/cjs/adapter/express/Responders.js +71 -0
- package/dist/cjs/adapter/express/StaticUI.d.ts +83 -0
- package/dist/cjs/adapter/express/StaticUI.js +104 -0
- package/dist/cjs/adapter/fastify/FastifyHTTPEntrypoint.d.ts +156 -0
- package/dist/cjs/adapter/fastify/FastifyHTTPEntrypoint.js +339 -0
- package/dist/cjs/adapter/fastify/Responders.d.ts +88 -0
- package/dist/cjs/adapter/fastify/Responders.js +77 -0
- package/dist/cjs/adapter/http/HTTPServiceEntrypoint.d.ts +117 -0
- package/dist/cjs/adapter/http/HTTPServiceEntrypoint.js +257 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCProtocol.d.ts +194 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCProtocol.js +187 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCServiceEntrypoint.d.ts +154 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCServiceEntrypoint.js +397 -0
- package/dist/cjs/adapter/schedule/ScheduleEntrypoint.d.ts +86 -0
- package/dist/cjs/adapter/schedule/ScheduleEntrypoint.js +275 -0
- package/dist/cjs/adapter/socket.io/SocketIOServiceEntrypoint.d.ts +138 -0
- package/dist/cjs/adapter/socket.io/SocketIOServiceEntrypoint.js +216 -0
- package/dist/cjs/channel/BuiltinChannels.d.ts +18 -0
- package/dist/cjs/channel/BuiltinChannels.js +128 -0
- package/dist/cjs/channel/Channel.d.ts +196 -0
- package/dist/cjs/channel/Channel.js +106 -0
- package/dist/cjs/channel/EntrypointAdapter.d.ts +195 -0
- package/dist/cjs/channel/EntrypointAdapter.js +43 -0
- package/dist/cjs/context/BaseContext.d.ts +75 -0
- package/dist/cjs/context/BaseContext.js +90 -0
- package/dist/cjs/context/CLIContext.d.ts +38 -0
- package/dist/cjs/context/CLIContext.js +53 -0
- package/dist/cjs/context/HTTPContext.d.ts +52 -0
- package/dist/cjs/context/HTTPContext.js +78 -0
- package/dist/cjs/context/ScheduleContext.d.ts +47 -0
- package/dist/cjs/context/ScheduleContext.js +65 -0
- package/dist/cjs/context/ServiceContext.d.ts +47 -0
- package/dist/cjs/context/ServiceContext.js +62 -0
- package/dist/cjs/decorators/CLIAction.d.ts +120 -0
- package/dist/cjs/decorators/CLIAction.js +13 -0
- package/dist/cjs/decorators/HTTPAction.d.ts +178 -0
- package/dist/cjs/decorators/HTTPAction.js +14 -0
- package/dist/cjs/decorators/Prefix.d.ts +44 -0
- package/dist/cjs/decorators/Prefix.js +48 -0
- package/dist/cjs/decorators/Schedule.d.ts +120 -0
- package/dist/cjs/decorators/Schedule.js +64 -0
- package/dist/cjs/decorators/ServiceAction.d.ts +116 -0
- package/dist/cjs/decorators/ServiceAction.js +12 -0
- package/dist/cjs/decorators/UseMiddlewares.d.ts +52 -0
- package/dist/cjs/decorators/UseMiddlewares.js +58 -0
- package/dist/cjs/decorators/http/DELETE.d.ts +87 -0
- package/dist/cjs/decorators/http/DELETE.js +7 -0
- package/dist/cjs/decorators/http/GET.d.ts +89 -0
- package/dist/cjs/decorators/http/GET.js +7 -0
- package/dist/cjs/decorators/http/HEAD.d.ts +84 -0
- package/dist/cjs/decorators/http/HEAD.js +7 -0
- package/dist/cjs/decorators/http/OPTIONS.d.ts +80 -0
- package/dist/cjs/decorators/http/OPTIONS.js +7 -0
- package/dist/cjs/decorators/http/PATCH.d.ts +90 -0
- package/dist/cjs/decorators/http/PATCH.js +7 -0
- package/dist/cjs/decorators/http/POST.d.ts +91 -0
- package/dist/cjs/decorators/http/POST.js +7 -0
- package/dist/cjs/decorators/http/PUT.d.ts +92 -0
- package/dist/cjs/decorators/http/PUT.js +7 -0
- package/dist/cjs/exceptions/AccessDeniedException.d.ts +10 -0
- package/dist/cjs/exceptions/AccessDeniedException.js +17 -0
- package/dist/cjs/exceptions/ControllerActionNotFoundException.d.ts +9 -0
- package/dist/cjs/exceptions/ControllerActionNotFoundException.js +16 -0
- package/dist/cjs/exceptions/DuplicateActionNameException.d.ts +8 -0
- package/dist/cjs/exceptions/DuplicateActionNameException.js +15 -0
- package/dist/cjs/exceptions/EntrypointStoppedException.d.ts +9 -0
- package/dist/cjs/exceptions/EntrypointStoppedException.js +16 -0
- package/dist/cjs/exceptions/InvalidActionGroupException.d.ts +4 -0
- package/dist/cjs/exceptions/InvalidActionGroupException.js +11 -0
- package/dist/cjs/exceptions/InvalidChannelException.d.ts +8 -0
- package/dist/cjs/exceptions/InvalidChannelException.js +15 -0
- package/dist/cjs/exceptions/InvalidEntrypointAdapterException.d.ts +9 -0
- package/dist/cjs/exceptions/InvalidEntrypointAdapterException.js +16 -0
- package/dist/cjs/exceptions/InvalidMiddlewareException.d.ts +8 -0
- package/dist/cjs/exceptions/InvalidMiddlewareException.js +15 -0
- package/dist/cjs/exceptions/InvalidPrefixException.d.ts +8 -0
- package/dist/cjs/exceptions/InvalidPrefixException.js +15 -0
- package/dist/cjs/exceptions/MiddlewareNextCalledException.d.ts +8 -0
- package/dist/cjs/exceptions/MiddlewareNextCalledException.js +15 -0
- package/dist/cjs/exceptions/RouteNotFoundException.d.ts +9 -0
- package/dist/cjs/exceptions/RouteNotFoundException.js +16 -0
- package/dist/cjs/exceptions/ServiceInvokeException.d.ts +11 -0
- package/dist/cjs/exceptions/ServiceInvokeException.js +14 -0
- package/dist/cjs/exports/CTRLDecorators.d.ts +13 -0
- package/dist/cjs/exports/CTRLDecorators.js +38 -0
- package/dist/cjs/exports/Channel.d.ts +14 -0
- package/dist/cjs/exports/Channel.js +33 -0
- package/dist/cjs/exports/CommanderAdapter.d.ts +1 -0
- package/dist/cjs/exports/CommanderAdapter.js +8 -0
- package/dist/cjs/exports/ElectronAdapter.d.ts +1 -0
- package/dist/cjs/exports/ElectronAdapter.js +9 -0
- package/dist/cjs/exports/Entrypoint.d.ts +24 -0
- package/dist/cjs/exports/Entrypoint.js +55 -0
- package/dist/cjs/exports/ExpressAdapter.d.ts +4 -0
- package/dist/cjs/exports/ExpressAdapter.js +15 -0
- package/dist/cjs/exports/FastifyAdapter.d.ts +4 -0
- package/dist/cjs/exports/FastifyAdapter.js +14 -0
- package/dist/cjs/exports/JSONRPCAdapter.d.ts +2 -0
- package/dist/cjs/exports/JSONRPCAdapter.js +11 -0
- package/dist/cjs/exports/SocketIOAdapter.d.ts +1 -0
- package/dist/cjs/exports/SocketIOAdapter.js +9 -0
- package/dist/cjs/lib/AccessControl.d.ts +118 -0
- package/dist/cjs/lib/AccessControl.js +208 -0
- package/dist/cjs/lib/AccessControlRule.d.ts +91 -0
- package/dist/cjs/lib/AccessControlRule.js +76 -0
- package/dist/cjs/lib/ActionOptions.d.ts +67 -0
- package/dist/cjs/lib/ActionOptions.js +67 -0
- package/dist/cjs/lib/ActionResult.d.ts +28 -0
- package/dist/cjs/lib/ActionResult.js +28 -0
- package/dist/cjs/lib/ApplicationOf.d.ts +6 -0
- package/dist/cjs/lib/ApplicationOf.js +14 -0
- package/dist/cjs/lib/Controller.d.ts +59 -0
- package/dist/cjs/lib/Controller.js +64 -0
- package/dist/cjs/lib/ControllerEntrypoint.d.ts +168 -0
- package/dist/cjs/lib/ControllerEntrypoint.js +239 -0
- package/dist/cjs/lib/GetActionDTOAndOptions.d.ts +29 -0
- package/dist/cjs/lib/GetActionDTOAndOptions.js +49 -0
- package/dist/cjs/lib/HTTPErrors.d.ts +55 -0
- package/dist/cjs/lib/HTTPErrors.js +52 -0
- package/dist/cjs/lib/Middleware.d.ts +146 -0
- package/dist/cjs/lib/Middleware.js +144 -0
- package/dist/cjs/lib/Prefix.d.ts +98 -0
- package/dist/cjs/lib/Prefix.js +151 -0
- package/dist/cjs/lib/Result.d.ts +173 -0
- package/dist/cjs/lib/Result.js +153 -0
- package/dist/cjs/lib/ServiceResponseCodec.d.ts +82 -0
- package/dist/cjs/lib/ServiceResponseCodec.js +117 -0
- package/dist/cjs/lib/StringifyPattern.d.ts +7 -0
- package/dist/cjs/lib/StringifyPattern.js +58 -0
- package/dist/cjs/lib/UnsetValue.d.ts +12 -0
- package/dist/cjs/lib/UnsetValue.js +216 -0
- package/dist/cjs/lib/pattern/GlobPattern.d.ts +34 -0
- package/dist/cjs/lib/pattern/GlobPattern.js +68 -0
- package/dist/cjs/lib/pattern/PatternManager.d.ts +103 -0
- package/dist/cjs/lib/pattern/PatternManager.js +233 -0
- package/dist/cjs/lib/schedule/Cron.d.ts +56 -0
- package/dist/cjs/lib/schedule/Cron.js +282 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types/CLIContextParams.d.ts +14 -0
- package/dist/cjs/types/CLIContextParams.js +2 -0
- package/dist/cjs/types/ErrorDescription.d.ts +14 -0
- package/dist/cjs/types/ErrorDescription.js +2 -0
- package/dist/cjs/types/HTTPContextParams.d.ts +27 -0
- package/dist/cjs/types/HTTPContextParams.js +2 -0
- package/dist/cjs/types/ScheduleContextParams.d.ts +21 -0
- package/dist/cjs/types/ScheduleContextParams.js +2 -0
- package/dist/cjs/types/ServiceContextParams.d.ts +16 -0
- package/dist/cjs/types/ServiceContextParams.js +2 -0
- package/dist/esm/Entrypoint.js +985 -0
- package/dist/esm/adapter/commander/CommanderCLIEntrypoint.js +433 -0
- package/dist/esm/adapter/commander/Prompt.js +127 -0
- package/dist/esm/adapter/commander/Width.js +63 -0
- package/dist/esm/adapter/electron/ElectronIPCServiceEntrypoint.js +212 -0
- package/dist/esm/adapter/express/ExpressHTTPEntrypoint.js +295 -0
- package/dist/esm/adapter/express/Responders.js +66 -0
- package/dist/esm/adapter/express/StaticUI.js +96 -0
- package/dist/esm/adapter/fastify/FastifyHTTPEntrypoint.js +333 -0
- package/dist/esm/adapter/fastify/Responders.js +73 -0
- package/dist/esm/adapter/http/HTTPServiceEntrypoint.js +253 -0
- package/dist/esm/adapter/jsonrpc/JSONRPCProtocol.js +182 -0
- package/dist/esm/adapter/jsonrpc/JSONRPCServiceEntrypoint.js +361 -0
- package/dist/esm/adapter/schedule/ScheduleEntrypoint.js +272 -0
- package/dist/esm/adapter/socket.io/SocketIOServiceEntrypoint.js +212 -0
- package/dist/esm/channel/BuiltinChannels.js +125 -0
- package/dist/esm/channel/Channel.js +100 -0
- package/dist/esm/channel/EntrypointAdapter.js +39 -0
- package/dist/esm/context/BaseContext.js +87 -0
- package/dist/esm/context/CLIContext.js +49 -0
- package/dist/esm/context/HTTPContext.js +74 -0
- package/dist/esm/context/ScheduleContext.js +61 -0
- package/dist/esm/context/ServiceContext.js +58 -0
- package/dist/esm/decorators/CLIAction.js +10 -0
- package/dist/esm/decorators/HTTPAction.js +11 -0
- package/dist/esm/decorators/Prefix.js +45 -0
- package/dist/esm/decorators/Schedule.js +61 -0
- package/dist/esm/decorators/ServiceAction.js +9 -0
- package/dist/esm/decorators/UseMiddlewares.js +55 -0
- package/dist/esm/decorators/http/DELETE.js +4 -0
- package/dist/esm/decorators/http/GET.js +4 -0
- package/dist/esm/decorators/http/HEAD.js +4 -0
- package/dist/esm/decorators/http/OPTIONS.js +4 -0
- package/dist/esm/decorators/http/PATCH.js +4 -0
- package/dist/esm/decorators/http/POST.js +4 -0
- package/dist/esm/decorators/http/PUT.js +4 -0
- package/dist/esm/exceptions/AccessDeniedException.js +13 -0
- package/dist/esm/exceptions/ControllerActionNotFoundException.js +12 -0
- package/dist/esm/exceptions/DuplicateActionNameException.js +11 -0
- package/dist/esm/exceptions/EntrypointStoppedException.js +12 -0
- package/dist/esm/exceptions/InvalidActionGroupException.js +7 -0
- package/dist/esm/exceptions/InvalidChannelException.js +11 -0
- package/dist/esm/exceptions/InvalidEntrypointAdapterException.js +12 -0
- package/dist/esm/exceptions/InvalidMiddlewareException.js +11 -0
- package/dist/esm/exceptions/InvalidPrefixException.js +11 -0
- package/dist/esm/exceptions/MiddlewareNextCalledException.js +11 -0
- package/dist/esm/exceptions/RouteNotFoundException.js +12 -0
- package/dist/esm/exceptions/ServiceInvokeException.js +10 -0
- package/dist/esm/exports/CTRLDecorators.js +19 -0
- package/dist/esm/exports/Channel.js +11 -0
- package/dist/esm/exports/CommanderAdapter.js +1 -0
- package/dist/esm/exports/ElectronAdapter.js +1 -0
- package/dist/esm/exports/Entrypoint.js +22 -0
- package/dist/esm/exports/ExpressAdapter.js +3 -0
- package/dist/esm/exports/FastifyAdapter.js +3 -0
- package/dist/esm/exports/JSONRPCAdapter.js +2 -0
- package/dist/esm/exports/SocketIOAdapter.js +1 -0
- package/dist/esm/lib/AccessControl.js +204 -0
- package/dist/esm/lib/AccessControlRule.js +72 -0
- package/dist/esm/lib/ActionOptions.js +63 -0
- package/dist/esm/lib/ActionResult.js +24 -0
- package/dist/esm/lib/ApplicationOf.js +11 -0
- package/dist/esm/lib/Controller.js +61 -0
- package/dist/esm/lib/ControllerEntrypoint.js +229 -0
- package/dist/esm/lib/GetActionDTOAndOptions.js +46 -0
- package/dist/esm/lib/HTTPErrors.js +48 -0
- package/dist/esm/lib/Middleware.js +134 -0
- package/dist/esm/lib/Prefix.js +143 -0
- package/dist/esm/lib/Result.js +147 -0
- package/dist/esm/lib/ServiceResponseCodec.js +113 -0
- package/dist/esm/lib/StringifyPattern.js +55 -0
- package/dist/esm/lib/UnsetValue.js +213 -0
- package/dist/esm/lib/pattern/GlobPattern.js +64 -0
- package/dist/esm/lib/pattern/PatternManager.js +229 -0
- package/dist/esm/lib/schedule/Cron.js +277 -0
- package/dist/esm/types/CLIContextParams.js +1 -0
- package/dist/esm/types/ErrorDescription.js +1 -0
- package/dist/esm/types/HTTPContextParams.js +1 -0
- package/dist/esm/types/ScheduleContextParams.js +1 -0
- package/dist/esm/types/ServiceContextParams.js +1 -0
- package/dist/types/Entrypoint.d.ts +868 -0
- package/dist/types/adapter/commander/CommanderCLIEntrypoint.d.ts +192 -0
- package/dist/types/adapter/commander/Prompt.d.ts +22 -0
- package/dist/types/adapter/commander/Width.d.ts +12 -0
- package/dist/types/adapter/electron/ElectronIPCServiceEntrypoint.d.ts +203 -0
- package/dist/types/adapter/express/ExpressHTTPEntrypoint.d.ts +172 -0
- package/dist/types/adapter/express/Responders.d.ts +89 -0
- package/dist/types/adapter/express/StaticUI.d.ts +83 -0
- package/dist/types/adapter/fastify/FastifyHTTPEntrypoint.d.ts +156 -0
- package/dist/types/adapter/fastify/Responders.d.ts +88 -0
- package/dist/types/adapter/http/HTTPServiceEntrypoint.d.ts +117 -0
- package/dist/types/adapter/jsonrpc/JSONRPCProtocol.d.ts +194 -0
- package/dist/types/adapter/jsonrpc/JSONRPCServiceEntrypoint.d.ts +154 -0
- package/dist/types/adapter/schedule/ScheduleEntrypoint.d.ts +86 -0
- package/dist/types/adapter/socket.io/SocketIOServiceEntrypoint.d.ts +138 -0
- package/dist/types/channel/BuiltinChannels.d.ts +18 -0
- package/dist/types/channel/Channel.d.ts +196 -0
- package/dist/types/channel/EntrypointAdapter.d.ts +195 -0
- package/dist/types/context/BaseContext.d.ts +75 -0
- package/dist/types/context/CLIContext.d.ts +38 -0
- package/dist/types/context/HTTPContext.d.ts +52 -0
- package/dist/types/context/ScheduleContext.d.ts +47 -0
- package/dist/types/context/ServiceContext.d.ts +47 -0
- package/dist/types/decorators/CLIAction.d.ts +120 -0
- package/dist/types/decorators/HTTPAction.d.ts +178 -0
- package/dist/types/decorators/Prefix.d.ts +44 -0
- package/dist/types/decorators/Schedule.d.ts +120 -0
- package/dist/types/decorators/ServiceAction.d.ts +116 -0
- package/dist/types/decorators/UseMiddlewares.d.ts +52 -0
- package/dist/types/decorators/http/DELETE.d.ts +87 -0
- package/dist/types/decorators/http/GET.d.ts +89 -0
- package/dist/types/decorators/http/HEAD.d.ts +84 -0
- package/dist/types/decorators/http/OPTIONS.d.ts +80 -0
- package/dist/types/decorators/http/PATCH.d.ts +90 -0
- package/dist/types/decorators/http/POST.d.ts +91 -0
- package/dist/types/decorators/http/PUT.d.ts +92 -0
- package/dist/types/exceptions/AccessDeniedException.d.ts +10 -0
- package/dist/types/exceptions/ControllerActionNotFoundException.d.ts +9 -0
- package/dist/types/exceptions/DuplicateActionNameException.d.ts +8 -0
- package/dist/types/exceptions/EntrypointStoppedException.d.ts +9 -0
- package/dist/types/exceptions/InvalidActionGroupException.d.ts +4 -0
- package/dist/types/exceptions/InvalidChannelException.d.ts +8 -0
- package/dist/types/exceptions/InvalidEntrypointAdapterException.d.ts +9 -0
- package/dist/types/exceptions/InvalidMiddlewareException.d.ts +8 -0
- package/dist/types/exceptions/InvalidPrefixException.d.ts +8 -0
- package/dist/types/exceptions/MiddlewareNextCalledException.d.ts +8 -0
- package/dist/types/exceptions/RouteNotFoundException.d.ts +9 -0
- package/dist/types/exceptions/ServiceInvokeException.d.ts +11 -0
- package/dist/types/exports/CTRLDecorators.d.ts +13 -0
- package/dist/types/exports/Channel.d.ts +14 -0
- package/dist/types/exports/CommanderAdapter.d.ts +1 -0
- package/dist/types/exports/ElectronAdapter.d.ts +1 -0
- package/dist/types/exports/Entrypoint.d.ts +24 -0
- package/dist/types/exports/ExpressAdapter.d.ts +4 -0
- package/dist/types/exports/FastifyAdapter.d.ts +4 -0
- package/dist/types/exports/JSONRPCAdapter.d.ts +2 -0
- package/dist/types/exports/SocketIOAdapter.d.ts +1 -0
- package/dist/types/lib/AccessControl.d.ts +118 -0
- package/dist/types/lib/AccessControlRule.d.ts +91 -0
- package/dist/types/lib/ActionOptions.d.ts +67 -0
- package/dist/types/lib/ActionResult.d.ts +28 -0
- package/dist/types/lib/ApplicationOf.d.ts +6 -0
- package/dist/types/lib/Controller.d.ts +59 -0
- package/dist/types/lib/ControllerEntrypoint.d.ts +168 -0
- package/dist/types/lib/GetActionDTOAndOptions.d.ts +29 -0
- package/dist/types/lib/HTTPErrors.d.ts +55 -0
- package/dist/types/lib/Middleware.d.ts +146 -0
- package/dist/types/lib/Prefix.d.ts +98 -0
- package/dist/types/lib/Result.d.ts +173 -0
- package/dist/types/lib/ServiceResponseCodec.d.ts +82 -0
- package/dist/types/lib/StringifyPattern.d.ts +7 -0
- package/dist/types/lib/UnsetValue.d.ts +12 -0
- package/dist/types/lib/pattern/GlobPattern.d.ts +34 -0
- package/dist/types/lib/pattern/PatternManager.d.ts +103 -0
- package/dist/types/lib/schedule/Cron.d.ts +56 -0
- package/dist/types/types/CLIContextParams.d.ts +14 -0
- package/dist/types/types/ErrorDescription.d.ts +14 -0
- package/dist/types/types/HTTPContextParams.d.ts +27 -0
- package/dist/types/types/ScheduleContextParams.d.ts +21 -0
- package/dist/types/types/ServiceContextParams.d.ts +16 -0
- package/package.json +152 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The glob matching of the values of the patterns, rewritten from gex (Copyright (c) 2011-2020 Richard Rodger, MIT
|
|
3
|
+
* License, see THIRD_PARTY_NOTICES.md)
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A glob of the value of a pattern: "*" matches any characters, "?" one character, "**" is a literal "*" and "*?" a
|
|
7
|
+
* literal "?". It matches a whole value, converted to a string
|
|
8
|
+
*/
|
|
9
|
+
export class GlobPattern {
|
|
10
|
+
/**
|
|
11
|
+
* The expression of the glob, undefined when it matches nothing (no glob)
|
|
12
|
+
*/
|
|
13
|
+
#expression;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a value of a pattern is a glob
|
|
16
|
+
* @param value
|
|
17
|
+
*/
|
|
18
|
+
static isGlob(value) {
|
|
19
|
+
return /[*?]/.test(value);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* @param glob the glob, nothing is matched without it
|
|
23
|
+
*/
|
|
24
|
+
constructor(glob) {
|
|
25
|
+
this.glob = glob;
|
|
26
|
+
//NaN is the empty glob, as gex cleans it
|
|
27
|
+
this.#expression = glob === null || glob === undefined ? undefined : GlobPattern.expression(Number.isNaN(glob) ? '' : String(glob));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The regular expression of a glob
|
|
31
|
+
* @param glob
|
|
32
|
+
*/
|
|
33
|
+
static expression(glob) {
|
|
34
|
+
let source = glob.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
35
|
+
//[\s\S] matches the line feeds too
|
|
36
|
+
source = source.replace(/\\\*/g, '[\\s\\S]*');
|
|
37
|
+
source = source.replace(/\\\?/g, '[\\s\\S]');
|
|
38
|
+
//"**" and "*?" are the literal "*" and "?"
|
|
39
|
+
source = source.replace(/\[\\s\\S\]\*\[\\s\\S\]\*/g, '\\*');
|
|
40
|
+
source = source.replace(/\[\\s\\S\]\*\[\\s\\S\]/g, '\\?');
|
|
41
|
+
return new RegExp(`^${source}$`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Whether the glob matches a value converted to a string
|
|
45
|
+
* @param value
|
|
46
|
+
*/
|
|
47
|
+
test(value) {
|
|
48
|
+
return !!this.#expression && this.#expression.test(String(value));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether the glob matches the value of a subject: a missing value is not matched, a primitive value (a date, a
|
|
52
|
+
* regular expression) is matched as a string. An array or an object is always matched, as gex does (it gives back
|
|
53
|
+
* the collection of its matching elements, which is never null): the routing of the actions is the one of 2.x
|
|
54
|
+
* @param value
|
|
55
|
+
*/
|
|
56
|
+
matches(value) {
|
|
57
|
+
if (value === null || value === undefined)
|
|
58
|
+
return false;
|
|
59
|
+
const type = typeof value;
|
|
60
|
+
if (type === 'string' || type === 'number' || type === 'boolean' || value instanceof Date || value instanceof RegExp)
|
|
61
|
+
return this.test(value);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The matching of the patterns, rewritten from patrun (Copyright (c) 2013-2022 Richard Rodger, MIT License, see
|
|
3
|
+
* THIRD_PARTY_NOTICES.md): the same decision tree, the same results
|
|
4
|
+
*/
|
|
5
|
+
import { GlobPattern } from './GlobPattern.js';
|
|
6
|
+
/**
|
|
7
|
+
* A dictionary without prototype: the values of the subjects are never the properties of Object ("constructor"…)
|
|
8
|
+
*/
|
|
9
|
+
function dictionary() {
|
|
10
|
+
return Object.create(null);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The patterns of the actions and the objects they give: a subject is matched by the most specific pattern whose
|
|
14
|
+
* properties it has (the unknown properties of the subject are ignored)
|
|
15
|
+
*/
|
|
16
|
+
export class PatternManager {
|
|
17
|
+
#globMatch;
|
|
18
|
+
#top = {};
|
|
19
|
+
constructor(options = { globMatch: true }) {
|
|
20
|
+
this.#globMatch = !!options.globMatch;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The glob of a value of a pattern, undefined when it is not one
|
|
24
|
+
* @param value
|
|
25
|
+
*/
|
|
26
|
+
glob(value) {
|
|
27
|
+
return this.#globMatch && GlobPattern.isGlob(value) ? new GlobPattern(value) : undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The node of a glob of a key of a node, added once
|
|
31
|
+
* @param node
|
|
32
|
+
* @param key
|
|
33
|
+
* @param glob
|
|
34
|
+
*/
|
|
35
|
+
globNode(node, key, glob) {
|
|
36
|
+
const globs = node.globs ??= dictionary();
|
|
37
|
+
const branches = globs[key] ??= [];
|
|
38
|
+
let branch = branches.find((candidate) => candidate.glob.glob === glob.glob);
|
|
39
|
+
if (!branch)
|
|
40
|
+
branches.push(branch = { glob: glob, node: {} });
|
|
41
|
+
return branch.node;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Register a pattern, and the object that will be returned if an input matches.
|
|
45
|
+
* Both keys and values are considered to be strings.
|
|
46
|
+
* Other types are converted to strings.
|
|
47
|
+
* @param pattern
|
|
48
|
+
* @param obj
|
|
49
|
+
*/
|
|
50
|
+
add(pattern, obj) {
|
|
51
|
+
const keys = Object.keys(pattern).filter((key) => pattern[key] !== null && pattern[key] !== undefined).sort();
|
|
52
|
+
const values = Object.fromEntries(keys.map((key) => [key, String(pattern[key])]));
|
|
53
|
+
let node = this.#top;
|
|
54
|
+
//The keys of the path are ordered: a key is inserted before the next key of the path, or follows the star nodes
|
|
55
|
+
for (let index = 0; index < keys.length; index++) {
|
|
56
|
+
const key = keys[index];
|
|
57
|
+
const value = values[key];
|
|
58
|
+
const glob = this.glob(value);
|
|
59
|
+
if (node.values && node.key === key) {
|
|
60
|
+
//The key of the node
|
|
61
|
+
node = glob ? this.globNode(node, key, glob) : node.values[value] ??= {};
|
|
62
|
+
}
|
|
63
|
+
else if (!node.key) {
|
|
64
|
+
//The end of the path: the key is the last one
|
|
65
|
+
node.key = key;
|
|
66
|
+
node.values = dictionary();
|
|
67
|
+
node = glob ? this.globNode(node, key, glob) : node.values[value] = {};
|
|
68
|
+
}
|
|
69
|
+
else if (key < node.key) {
|
|
70
|
+
//The key orders before the key of the node: the node moves to the star node of the key
|
|
71
|
+
const { star, globs } = node;
|
|
72
|
+
node.star = { key: node.key, values: node.values };
|
|
73
|
+
if (star)
|
|
74
|
+
node.star.star = star;
|
|
75
|
+
if (globs)
|
|
76
|
+
node.star.globs = globs;
|
|
77
|
+
if (node.globs)
|
|
78
|
+
node.globs = dictionary();
|
|
79
|
+
node.key = key;
|
|
80
|
+
node.values = dictionary();
|
|
81
|
+
node = glob ? this.globNode(node, key, glob) : node.values[value] = {};
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
//The key orders after the key of the node: it is in the path of the patterns without the key of the node
|
|
85
|
+
node = node.star ??= {};
|
|
86
|
+
index--;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (obj !== undefined) {
|
|
90
|
+
node.data = obj;
|
|
91
|
+
node.hasData = true;
|
|
92
|
+
}
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Remove this pattern, and it's object, from the matcher.
|
|
97
|
+
* @param pattern
|
|
98
|
+
*/
|
|
99
|
+
remove(pattern) {
|
|
100
|
+
let node = this.#top;
|
|
101
|
+
let data = undefined;
|
|
102
|
+
let hasData = false;
|
|
103
|
+
let last = undefined;
|
|
104
|
+
do {
|
|
105
|
+
const key = node.key;
|
|
106
|
+
if (node.values || node.globs) {
|
|
107
|
+
let next = node.values?.[pattern[key]];
|
|
108
|
+
if (next)
|
|
109
|
+
last = next;
|
|
110
|
+
if (!next && node.globs) {
|
|
111
|
+
const branch = (node.globs[key] ?? []).find((candidate) => candidate.glob.glob === pattern[key]);
|
|
112
|
+
if (branch)
|
|
113
|
+
last = next = branch.node;
|
|
114
|
+
}
|
|
115
|
+
if (next) {
|
|
116
|
+
data = next.data;
|
|
117
|
+
hasData = !!next.hasData;
|
|
118
|
+
node = next;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
node = node.star;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
node = undefined;
|
|
126
|
+
}
|
|
127
|
+
} while (node);
|
|
128
|
+
if (hasData && data !== undefined && last) {
|
|
129
|
+
delete last.data;
|
|
130
|
+
delete last.hasData;
|
|
131
|
+
}
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Return the unique match for this subject, or null if not found.
|
|
136
|
+
* The properties of the subject are matched against the patterns previously added, and the most specifc pattern wins.
|
|
137
|
+
* Unknown properties in the subject are ignored.
|
|
138
|
+
* If exact is true, then all properties of the subject must match.
|
|
139
|
+
* @param subject
|
|
140
|
+
* @param exact
|
|
141
|
+
*/
|
|
142
|
+
find(subject, exact = false) {
|
|
143
|
+
if (subject === null || subject === undefined)
|
|
144
|
+
return null;
|
|
145
|
+
const top = this.#top;
|
|
146
|
+
let node = top;
|
|
147
|
+
let data = top.hasData ? top.data : null;
|
|
148
|
+
const stars = [];
|
|
149
|
+
const found = exact ? new Set() : undefined;
|
|
150
|
+
do {
|
|
151
|
+
const key = node.key;
|
|
152
|
+
if (node.values) {
|
|
153
|
+
const value = subject[key];
|
|
154
|
+
//The value itself (converted to a string, as the key of a dictionary), else a glob of it
|
|
155
|
+
let next = node.values[value];
|
|
156
|
+
if (!next && node.globs?.[key])
|
|
157
|
+
next = node.globs[key].find((branch) => branch.glob.matches(value))?.node;
|
|
158
|
+
if (next) {
|
|
159
|
+
found?.add(key);
|
|
160
|
+
if (node.star)
|
|
161
|
+
stars.push(node.star);
|
|
162
|
+
data = next.hasData ? next.data : (exact ? null : data);
|
|
163
|
+
node = next;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
//The patterns without the key
|
|
167
|
+
node = node.star;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
node = undefined;
|
|
172
|
+
}
|
|
173
|
+
//The wider patterns, when the narrower ones gave nothing
|
|
174
|
+
if (!node && stars.length && (data === null || data === undefined))
|
|
175
|
+
node = stars.pop();
|
|
176
|
+
} while (node);
|
|
177
|
+
if (exact) {
|
|
178
|
+
if (found.size !== Object.keys(subject).length)
|
|
179
|
+
data = null;
|
|
180
|
+
}
|
|
181
|
+
else if ((data === null || data === undefined) && top.hasData) {
|
|
182
|
+
//The object of the empty pattern matches everything
|
|
183
|
+
data = top.data;
|
|
184
|
+
}
|
|
185
|
+
return data;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Return the list of registered patterns that contain this partial pattern.
|
|
189
|
+
* You can use wildcards for property values.
|
|
190
|
+
* Omitted values are not equivalent to a wildcard of "*", you must specify each property explicitly.
|
|
191
|
+
* @param partialPattern
|
|
192
|
+
* @param exact the omitted properties match nothing
|
|
193
|
+
*/
|
|
194
|
+
list(partialPattern, exact = false) {
|
|
195
|
+
const pattern = partialPattern ?? {};
|
|
196
|
+
const results = [];
|
|
197
|
+
const descend = (node, match, missing) => {
|
|
198
|
+
if (!node.values)
|
|
199
|
+
return;
|
|
200
|
+
const key = node.key;
|
|
201
|
+
const glob = new GlobPattern(pattern[key] === null || pattern[key] === undefined ? (exact ? null : '*') : pattern[key]);
|
|
202
|
+
for (const value in node.values) {
|
|
203
|
+
//The empty value is not matched by a glob (gex gives back the matched value, which is falsy)
|
|
204
|
+
if (value === pattern[key] || (!exact && (pattern[key] === null || pattern[key] === undefined)) || (value !== '' && glob.test(value))) {
|
|
205
|
+
const valueMatch = { ...match, [key]: value };
|
|
206
|
+
const valueMissing = { ...missing };
|
|
207
|
+
delete valueMissing[key];
|
|
208
|
+
const next = node.values[value];
|
|
209
|
+
if (!Object.keys(valueMissing).length && next.hasData && next.data)
|
|
210
|
+
results.push({ match: valueMatch, data: next.data });
|
|
211
|
+
if (next.values)
|
|
212
|
+
descend(next, { ...valueMatch }, { ...valueMissing });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (node.star)
|
|
216
|
+
descend(node.star, { ...match }, { ...missing });
|
|
217
|
+
};
|
|
218
|
+
if (this.#top.hasData && this.#top.data)
|
|
219
|
+
results.push({ match: {}, data: this.#top.data });
|
|
220
|
+
descend(this.#top, {}, { ...pattern });
|
|
221
|
+
return results;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Generate a string representation of the decision tree for debugging.
|
|
225
|
+
*/
|
|
226
|
+
toJSON() {
|
|
227
|
+
return JSON.stringify(this.#top, (key, value) => value instanceof GlobPattern ? String(value.glob) : (key === 'hasData' ? undefined : value));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
const MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
|
|
2
|
+
const WEEKDAYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
|
|
3
|
+
const MACROS = {
|
|
4
|
+
'@yearly': '0 0 0 1 1 *',
|
|
5
|
+
'@annually': '0 0 0 1 1 *',
|
|
6
|
+
'@monthly': '0 0 0 1 * *',
|
|
7
|
+
'@weekly': '0 0 0 * * 0',
|
|
8
|
+
'@daily': '0 0 0 * * *',
|
|
9
|
+
'@midnight': '0 0 0 * * *',
|
|
10
|
+
'@hourly': '0 0 * * * *'
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The formatters of the time zones
|
|
14
|
+
*/
|
|
15
|
+
const formatters = new Map();
|
|
16
|
+
/**
|
|
17
|
+
* The wall clock time of an instant, in a time zone or in the local time
|
|
18
|
+
* @param time
|
|
19
|
+
* @param timezone
|
|
20
|
+
*/
|
|
21
|
+
function wallClock(time, timezone) {
|
|
22
|
+
if (!timezone) {
|
|
23
|
+
const date = new Date(time);
|
|
24
|
+
return {
|
|
25
|
+
year: date.getFullYear(),
|
|
26
|
+
month: date.getMonth() + 1,
|
|
27
|
+
day: date.getDate(),
|
|
28
|
+
hour: date.getHours(),
|
|
29
|
+
minute: date.getMinutes(),
|
|
30
|
+
second: date.getSeconds(),
|
|
31
|
+
weekday: date.getDay()
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
let formatter = formatters.get(timezone);
|
|
35
|
+
if (!formatter) {
|
|
36
|
+
formatter = new Intl.DateTimeFormat('en-US', {
|
|
37
|
+
timeZone: timezone,
|
|
38
|
+
hourCycle: 'h23',
|
|
39
|
+
year: 'numeric',
|
|
40
|
+
month: 'numeric',
|
|
41
|
+
day: 'numeric',
|
|
42
|
+
hour: 'numeric',
|
|
43
|
+
minute: 'numeric',
|
|
44
|
+
second: 'numeric',
|
|
45
|
+
weekday: 'short'
|
|
46
|
+
});
|
|
47
|
+
formatters.set(timezone, formatter);
|
|
48
|
+
}
|
|
49
|
+
const parts = {};
|
|
50
|
+
for (const part of formatter.formatToParts(new Date(time)))
|
|
51
|
+
parts[part.type] = part.value;
|
|
52
|
+
return {
|
|
53
|
+
year: Number(parts.year),
|
|
54
|
+
month: Number(parts.month),
|
|
55
|
+
day: Number(parts.day),
|
|
56
|
+
hour: Number(parts.hour),
|
|
57
|
+
minute: Number(parts.minute),
|
|
58
|
+
second: Number(parts.second),
|
|
59
|
+
weekday: WEEKDAYS.indexOf(parts.weekday.toLowerCase())
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A wall clock time as a number, to compare them
|
|
64
|
+
* @param clock
|
|
65
|
+
*/
|
|
66
|
+
function wallKey(clock) {
|
|
67
|
+
return Date.UTC(clock.year, clock.month - 1, clock.day, clock.hour, clock.minute, clock.second);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The instant of a wall clock time (the overflowing fields carry over), in a time zone or in the local time
|
|
71
|
+
* @param year
|
|
72
|
+
* @param month
|
|
73
|
+
* @param day
|
|
74
|
+
* @param hour
|
|
75
|
+
* @param minute
|
|
76
|
+
* @param second
|
|
77
|
+
* @param timezone
|
|
78
|
+
*/
|
|
79
|
+
function instant(year, month, day, hour, minute, second, timezone) {
|
|
80
|
+
if (!timezone)
|
|
81
|
+
return new Date(year, month - 1, day, hour, minute, second).getTime();
|
|
82
|
+
const wall = Date.UTC(year, month - 1, day, hour, minute, second);
|
|
83
|
+
//The offset of the time zone at the instant, found again at the corrected instant (the offset changes with the daylight saving time)
|
|
84
|
+
let time = wall;
|
|
85
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
86
|
+
const clock = wallClock(time, timezone);
|
|
87
|
+
const offset = Date.UTC(clock.year, clock.month - 1, clock.day, clock.hour, clock.minute, clock.second) - time;
|
|
88
|
+
time = wall - offset;
|
|
89
|
+
}
|
|
90
|
+
return time;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse a field of an expression
|
|
94
|
+
* @param text
|
|
95
|
+
* @param min
|
|
96
|
+
* @param max
|
|
97
|
+
* @param names the names of the values from min
|
|
98
|
+
* @param expression
|
|
99
|
+
*/
|
|
100
|
+
function parseField(text, min, max, names, expression) {
|
|
101
|
+
const invalid = (reason) => new SyntaxError(`Invalid cron expression "${expression}": ${reason}`);
|
|
102
|
+
const value = (item) => {
|
|
103
|
+
const index = names ? names.indexOf(item.toLowerCase()) : -1;
|
|
104
|
+
const parsed = index >= 0 ? index + min : /^\d+$/.test(item) ? Number(item) : NaN;
|
|
105
|
+
if (Number.isNaN(parsed) || parsed < min || parsed > max)
|
|
106
|
+
throw invalid(`"${item}" is not a value from ${min} to ${max}`);
|
|
107
|
+
return parsed;
|
|
108
|
+
};
|
|
109
|
+
const values = new Set();
|
|
110
|
+
const restricted = text !== '*' && text !== '?';
|
|
111
|
+
for (const item of text.split(',')) {
|
|
112
|
+
const [range, stepText] = item.split('/');
|
|
113
|
+
const step = stepText === undefined ? 1 : Number(stepText);
|
|
114
|
+
if (!Number.isInteger(step) || step < 1)
|
|
115
|
+
throw invalid(`"${stepText}" is not a step`);
|
|
116
|
+
let start = min;
|
|
117
|
+
let end = max;
|
|
118
|
+
if (range !== '*' && range !== '?') {
|
|
119
|
+
const [from, to] = range.split('-');
|
|
120
|
+
start = value(from);
|
|
121
|
+
end = to === undefined ? (stepText === undefined ? start : max) : value(to);
|
|
122
|
+
if (end < start)
|
|
123
|
+
throw invalid(`the range "${range}" is reversed`);
|
|
124
|
+
}
|
|
125
|
+
for (let current = start; current <= end; current += step)
|
|
126
|
+
values.add(current);
|
|
127
|
+
}
|
|
128
|
+
return { values: values, restricted: restricted };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A cron expression, as `@Schedule` uses it: `second minute hour day-of-month month day-of-week`, the second optional
|
|
132
|
+
* (0 when omitted). Each field accepts `*` (or `?`), values, ranges (`1-5`), steps (`*\/15`, `10/5`, `1-30/2`) and lists
|
|
133
|
+
* (`1,15`); the months and the days of the week also accept their English three-letter names (`jan`, `mon`), and the
|
|
134
|
+
* day of the week 7 is Sunday like 0. The macros `@yearly`, `@annually`, `@monthly`, `@weekly`, `@daily`, `@midnight`
|
|
135
|
+
* and `@hourly` are accepted. When both the day of the month and the day of the week are restricted, a day matching
|
|
136
|
+
* either of them matches.
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* import {Cron} from 'lakutata/com/entrypoint'
|
|
140
|
+
*
|
|
141
|
+
* //At 09:30 on the weekdays, in Shanghai
|
|
142
|
+
* const cron: Cron = new Cron('30 9 * * mon-fri', 'Asia/Shanghai')
|
|
143
|
+
* const next: Date | undefined = cron.next(new Date('2026-01-02T12:00:00Z'))
|
|
144
|
+
* console.log(next?.toISOString()) //2026-01-05T01:30:00.000Z
|
|
145
|
+
* console.log(new Cron('0 0 30 2 *').next()) //undefined: February has no 30th
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export class Cron {
|
|
149
|
+
#second;
|
|
150
|
+
#minute;
|
|
151
|
+
#hour;
|
|
152
|
+
#day;
|
|
153
|
+
#month;
|
|
154
|
+
#weekday;
|
|
155
|
+
/**
|
|
156
|
+
* Parse a cron expression.
|
|
157
|
+
* @param expression The expression, 5 or 6 fields separated by spaces, or a macro (case-insensitive).
|
|
158
|
+
* @param timezone The IANA time zone the expression's times are in (`Asia/Shanghai`); the local time zone when
|
|
159
|
+
* omitted.
|
|
160
|
+
* @throws {SyntaxError} When the expression does not have 5 or 6 fields, or a field has a value out of its range,
|
|
161
|
+
* a reversed range or an invalid step.
|
|
162
|
+
* @throws {RangeError} When the time zone is unknown.
|
|
163
|
+
*/
|
|
164
|
+
constructor(expression, timezone) {
|
|
165
|
+
this.expression = expression;
|
|
166
|
+
this.timezone = timezone;
|
|
167
|
+
if (timezone)
|
|
168
|
+
new Intl.DateTimeFormat('en-US', { timeZone: timezone });
|
|
169
|
+
const normalized = expression.trim().toLowerCase();
|
|
170
|
+
const fields = (MACROS[normalized] ?? normalized).split(/\s+/);
|
|
171
|
+
if (fields.length === 5)
|
|
172
|
+
fields.unshift('0');
|
|
173
|
+
if (fields.length !== 6)
|
|
174
|
+
throw new SyntaxError(`Invalid cron expression "${expression}": 5 or 6 fields are expected`);
|
|
175
|
+
this.#second = parseField(fields[0], 0, 59, undefined, expression);
|
|
176
|
+
this.#minute = parseField(fields[1], 0, 59, undefined, expression);
|
|
177
|
+
this.#hour = parseField(fields[2], 0, 23, undefined, expression);
|
|
178
|
+
this.#day = parseField(fields[3], 1, 31, undefined, expression);
|
|
179
|
+
this.#month = parseField(fields[4], 1, 12, MONTHS, expression);
|
|
180
|
+
const weekday = parseField(fields[5], 0, 7, WEEKDAYS, expression);
|
|
181
|
+
//7 is Sunday too
|
|
182
|
+
if (weekday.values.delete(7))
|
|
183
|
+
weekday.values.add(0);
|
|
184
|
+
this.#weekday = weekday;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Whether a day matches
|
|
188
|
+
* @param clock
|
|
189
|
+
*/
|
|
190
|
+
#dayMatches(clock) {
|
|
191
|
+
const day = this.#day.values.has(clock.day);
|
|
192
|
+
const weekday = this.#weekday.values.has(clock.weekday);
|
|
193
|
+
if (this.#day.restricted && this.#weekday.restricted)
|
|
194
|
+
return day || weekday;
|
|
195
|
+
return day && weekday;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Find the next time matching the expression, strictly after a time, to the second. When the clocks go back, the
|
|
199
|
+
* times of the repeated hour match once; the times skipped when the clocks go forward never match.
|
|
200
|
+
* @param after The time to search from, a date or milliseconds since the epoch; now by default.
|
|
201
|
+
* @returns The next matching time, `undefined` when none matches within eight years (such as February 30th).
|
|
202
|
+
*/
|
|
203
|
+
next(after = Date.now()) {
|
|
204
|
+
const timezone = this.timezone;
|
|
205
|
+
const start = Math.floor(new Date(after).getTime() / 1000) * 1000;
|
|
206
|
+
//When the clocks go back, the times of the repeated hour match once
|
|
207
|
+
const afterKey = wallKey(wallClock(start, timezone));
|
|
208
|
+
let time = start + 1000;
|
|
209
|
+
//Eight years of candidates at most: a leap day on a weekday comes back within them
|
|
210
|
+
const limit = time + 8 * 366 * 24 * 3600 * 1000;
|
|
211
|
+
while (time < limit) {
|
|
212
|
+
const clock = wallClock(time, timezone);
|
|
213
|
+
let candidate;
|
|
214
|
+
if (!this.#month.values.has(clock.month)) {
|
|
215
|
+
candidate = instant(clock.year, clock.month + 1, 1, 0, 0, 0, timezone);
|
|
216
|
+
}
|
|
217
|
+
else if (!this.#dayMatches(clock)) {
|
|
218
|
+
candidate = instant(clock.year, clock.month, clock.day + 1, 0, 0, 0, timezone);
|
|
219
|
+
}
|
|
220
|
+
else if (!this.#hour.values.has(clock.hour)) {
|
|
221
|
+
candidate = instant(clock.year, clock.month, clock.day, clock.hour + 1, 0, 0, timezone);
|
|
222
|
+
}
|
|
223
|
+
else if (!this.#minute.values.has(clock.minute)) {
|
|
224
|
+
candidate = instant(clock.year, clock.month, clock.day, clock.hour, clock.minute + 1, 0, timezone);
|
|
225
|
+
}
|
|
226
|
+
else if (!this.#second.values.has(clock.second)) {
|
|
227
|
+
candidate = instant(clock.year, clock.month, clock.day, clock.hour, clock.minute, clock.second + 1, timezone);
|
|
228
|
+
}
|
|
229
|
+
else if (wallKey(clock) <= afterKey) {
|
|
230
|
+
candidate = time + (afterKey - wallKey(clock)) + 1000;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
return new Date(time);
|
|
234
|
+
}
|
|
235
|
+
//A time skipped by the daylight saving time may map back: the search always moves forward
|
|
236
|
+
time = candidate > time ? candidate : time + 1000;
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Parse a duration into milliseconds, as `@Schedule` reads its intervals.
|
|
243
|
+
* @param duration A number of milliseconds, or a text of numbers with their units (`ms`, `s`, `m`, `h`, `d`), summed
|
|
244
|
+
* and case-insensitive: `500ms`, `30s`, `1.5h`, `1h 30m`, `1d`.
|
|
245
|
+
* @returns The duration in milliseconds.
|
|
246
|
+
* @throws {SyntaxError} When the duration is not positive (or not finite), or the text has an unknown part.
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* import {parseDuration} from 'lakutata/com/entrypoint'
|
|
250
|
+
*
|
|
251
|
+
* console.log(parseDuration('1h30m')) //5400000
|
|
252
|
+
* console.log(parseDuration('500ms')) //500
|
|
253
|
+
* console.log(parseDuration(2000)) //2000
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
export function parseDuration(duration) {
|
|
257
|
+
if (typeof duration === 'number') {
|
|
258
|
+
if (!Number.isFinite(duration) || duration <= 0)
|
|
259
|
+
throw new SyntaxError(`Invalid duration ${duration}: a positive number of milliseconds is expected`);
|
|
260
|
+
return duration;
|
|
261
|
+
}
|
|
262
|
+
const units = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
|
|
263
|
+
const text = duration.trim().toLowerCase();
|
|
264
|
+
let total = 0;
|
|
265
|
+
let rest = text;
|
|
266
|
+
const part = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)/;
|
|
267
|
+
while (rest) {
|
|
268
|
+
const matched = part.exec(rest);
|
|
269
|
+
if (!matched)
|
|
270
|
+
throw new SyntaxError(`Invalid duration "${duration}": such as "500ms", "30s", "5m", "1h30m" or "1d"`);
|
|
271
|
+
total += Number(matched[1]) * units[matched[2]];
|
|
272
|
+
rest = rest.slice(matched[0].length).trim();
|
|
273
|
+
}
|
|
274
|
+
if (total <= 0)
|
|
275
|
+
throw new SyntaxError(`Invalid duration "${duration}": it must be positive`);
|
|
276
|
+
return total;
|
|
277
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|