// DSL OFF export * from './src/index'; import { backfillArgs, decoratorHandler, errorCheck, grabActualResult, must, punchGrab, RunAsyncController, should, TaggedTemplateSelfChain, TimeUnit, unitTimeMs, __ScopeContext, testDefine, PostfixReturn } from './src/index'; import { CallContextEvent, getRuntime, __BlockContext, __FunctionContext, __RuntimeContext } from './src/lib/runtime.context'; import { DSL, dslIfaceGuard, dslUntransformedGuard } from './src/lib/dsl'; import { Class, MergeClass, PromiseCollapse, promise } from 'ts-basis'; import { version } from './package.json'; export * from './src/lib/tester'; export { fromDSL } from './src/lib/dsl-util.misc'; export function DSL_ON() {} export function DSL_SKIP() {} export const dslVersion = version; DSL.compilerVersion = dslVersion; // ==================================================== // Facade // ==================================================== export function __dsl_reflect(...arg): any { return dslIfaceGuard('__dsl_reflect', __filename); } export function context(stepName?: string): any { return dslIfaceGuard('context', __filename); } export function safe(stepName?: string): any { return dslIfaceGuard('safe', __filename); } export function noInterrupt(stepName?: string): any { return dslIfaceGuard('noInterrupt', __filename); } function runModel any = any>(logic?: T): PostfixReturn>> { return dslIfaceGuard('run', __filename); } export const run = runModel as (typeof runModel & Class); function asyncModel any = any>(timeInSeconds: number, logic?: T): PostfixReturn; function asyncModel any = any>(time: number, unit: TimeUnit, logic?: T): PostfixReturn; function asyncModel(...arg): PostfixReturn { return dslIfaceGuard('async', __filename); } export const async = asyncModel as (typeof asyncModel & Class); function runDeferModel any = any>(timeInSeconds: number, logic?: T): PostfixReturn; function runDeferModel any = any>(time: number, unit: TimeUnit, logic?: T): PostfixReturn; function runDeferModel(...arg): PostfixReturn { return dslIfaceGuard('defer', __filename); } export const defer = runDeferModel as (typeof runDeferModel & Class); function runEveryModel any = any>(timeinSeconds: number, logic?: T): PostfixReturn; function runEveryModel any = any>(time: number, unit: TimeUnit, logic?: T): PostfixReturn; function runEveryModel(...arg): PostfixReturn { return dslIfaceGuard('every', __filename); } export const every = runEveryModel as (typeof runEveryModel & Class); export function sleep(duration: number, unit: TimeUnit = 's') { return dslIfaceGuard('sleep', __filename); } export function msleep(duration: number) { return dslIfaceGuard('msleep', __filename); } export function check(value: any) { return dslIfaceGuard('check', __filename); } export function timeout(duration: number, unit: TimeUnit = 's') { return dslIfaceGuard('timeout', __filename); } export namespace dsl { export let enabled = true; } // ==================================================== // Implemenation // ==================================================== export function __dsl_reflect__(...args): any { if (args[0] !== true) { dslUntransformedGuard('__dsl_reflect', __filename); } return decoratorHandler(args, { member: (target, deco) => { deco.addInitializer(function() { const sourceClass = this.constructor; if (!sourceClass['__dsl_reflect']) { Object.defineProperty(sourceClass, '__dsl_reflect', { value: {}, enumerable: false }); } sourceClass['__dsl_reflect'][args[2]] = args[3][0]; }); return target; } }); } let anonContextIndex = 0; export function context__(cce: CallContextEvent, ...args): any { const a = backfillArgs(args, 'contextName', 'contextClosure'); if (!a.contextName) { a.contextName = `(anonymous_context_${anonContextIndex++})`; } return getRuntime().scopedExec( cce, a.contextName, { data: { contextName: a.contextName } }, async (resolve, reject, scopeContext) => { try { let res; if (a.contextClosure) { res = await errorCheck(a.contextClosure(scopeContext)); } resolve(res); } catch (e) { reject(e); } }); } export function run__(cce: CallContextEvent, ...args): any { const a = backfillArgs(args, 'runClosure'); return getRuntime().scopedExec( cce, 'run', { data: {} }, async (resolve, reject, scopeContext) => { try { let res; if (a.runClosure) { res = await errorCheck(a.runClosure(scopeContext)); } resolve(res); } catch (e) { reject(e); } }); } export function async__(cce: CallContextEvent, ...args): any { return defer__(cce, ...args); } export function defer__(cce: CallContextEvent, ...args): any { let a; if (typeof args[0] === 'number' && typeof args[1] === 'string') { a = { dur: args[0], unit: args[1], runClosure: args[2]}; } else if (typeof args[0] === 'number') { a = { dur: args[0], unit: 's', runClosure: args[1]}; } else { a = { dur: 0, unit: 's', runClosure: args[0]}; } let asyncProm; let asyncPromResolver; const controller: RunAsyncController = { startTime: Date.now(), canceled: false, finished: false, result: undefined, errors: [], errorLast: null, errorsMaxKept: 10, join: async () => { return await asyncProm as any }, cancel: () => { if (controller.canceled) { return; } controller.canceled = true; asyncPromResolver(); return controller; }, }; let ctx: __ScopeContext; let asyncPromResolverCalled = false; asyncProm = promise(async ( resolve ) => { asyncPromResolver = (e?: Error) => { if (asyncPromResolverCalled) { return; } asyncPromResolverCalled = true; controller.endTime = Date.now(); controller.duration = controller.endTime - controller.startTime; if (ctx) { if (e) { getRuntime().setScopeError(ctx, null, e); } getRuntime().endContext(ctx); } resolve(); }; }); promise(async ( resolve, reject ) => { ctx = await getRuntime().newContext(cce, 'async-defer', cce.scopeContext, {}); try { resolve(controller); (async () => { await sleep__(cce, a.dur, a.unit); try { if (a.runClosure && !controller.canceled) { controller.result = await errorCheck(a.runClosure(ctx)); } } catch (e) { if (controller.errors.length < controller.errorsMaxKept) { controller.errors.push(e); } controller.errorLast = e; } asyncPromResolver(); })(); } catch (e) { asyncPromResolver(e); reject(e); } }); return controller; } export function every__(cce: CallContextEvent, ...args): any { let a; if (typeof args[0] === 'number' && typeof args[1] === 'string') { a = { dur: args[0], unit: args[1], runClosure: args[2]}; } else if (typeof args[0] === 'number') { a = { dur: args[0], unit: 's', runClosure: args[1]}; } else { a = { dur: 0, unit: 's', runClosure: args[0]}; } let asyncProm; let asyncPromResolver; const controller: RunAsyncController = { startTime: Date.now(), canceled: false, finished: false, result: undefined, errors: [], errorLast: null, errorsMaxKept: 10, join: async () => { return await asyncProm as any }, cancel: () => { if (controller.canceled) { return; } controller.canceled = true; asyncPromResolver(); return controller; }, }; let asyncPromResolverCalled = false; asyncProm = promise(async ( resolve ) => { asyncPromResolver = (e?: Error) => { if (asyncPromResolverCalled) { return; } asyncPromResolverCalled = true; controller.endTime = Date.now(); controller.duration = controller.endTime - controller.startTime; if (ctx) { if (e) { getRuntime().setScopeError(ctx, null, e); } getRuntime().endContext(ctx); } resolve(); }; }); let ctx: __ScopeContext; promise(async ( resolve, reject ) => { ctx = await getRuntime().newContext(cce, 'every', cce.scopeContext, {}); try { resolve(controller); (async () => { while (true) { if (controller.canceled) { break; } await sleep__(cce, a.dur, a.unit); try { if (a.runClosure && !controller.canceled) { controller.result = await errorCheck(a.runClosure(ctx)); } } catch (e) { if (controller.errors.length < controller.errorsMaxKept) { controller.errors.push(e); } controller.errorLast = e; } } asyncPromResolver(); })(); } catch (e) { asyncPromResolver(e); reject(e); } }); return controller; } export function safe__(cce: CallContextEvent, ...args): any { const a = backfillArgs(args, 'safeClosure'); return getRuntime().scopedExec( cce, 'safe', { data: {} }, async (resolve, reject, scopeContext) => { scopeContext.noInterrupt = true; try { let res; if (a.safeClosure) { res = await errorCheck(a.safeClosure(scopeContext)); } resolve(res); } catch (e) { reject(e); } }); } export function noInterrupt__(cce: CallContextEvent, ...args): any { const a = backfillArgs(args, 'safeClosure'); return getRuntime().scopedExec( cce, 'noInterrupt', { data: {} }, async (resolve, reject, scopeContext) => { scopeContext.noInterrupt = true; try { let res; if (a.safeClosure) { res = await errorCheck(a.safeClosure(scopeContext)); } resolve(res); } catch (e) { reject(e); } }); } export function timeout__(cce: CallContextEvent, ...args): any { let dur: number = 0; let unit: TimeUnit = 's'; let timeoutClosure: (ctx: __ScopeContext) => any; if (typeof args[0] === 'number' && typeof args[1] === 'string') { dur = args[0]; unit = args[1] as TimeUnit; timeoutClosure = args[2]; } else if (typeof args[0] === 'number' && typeof args[1] === 'function') { dur = args[0]; timeoutClosure = args[1]; } else { dur = args[0]; } let ms = unitTimeMs(dur, unit); if (!timeoutClosure) { if (cce.scopeContext.elapsed > ms) { const timeoutError = new Error(`[FlowControl] timed out after ${dur}${unit} in expression: ${cce.blockContext?.lastExpression}`); getRuntime().setScopeError(cce.scopeContext, cce.blockContext, timeoutError); throw timeoutError; } else { return true; } } const execObj = getRuntime().scopedExec( cce, 'timeout', { data: {} }, async (resolve, reject, scopeContext) => { try { let immediateBlock: __BlockContext; scopeContext.onImmediateBlockContextAssign.push((scopeCtx, blockCtx) => { immediateBlock = blockCtx; }); let res; let fulfilled = false; let returned = false; setTimeout(async () => { if (returned || fulfilled) { return; } const timeoutError = new Error(`[FlowControl] timed out after ${dur}${unit} in expression: ${immediateBlock?.lastExpression}`); getRuntime().setScopeError(scopeContext, immediateBlock, timeoutError); returned = true; return reject(timeoutError); }, ms); res = await errorCheck(timeoutClosure(scopeContext)); fulfilled = true; if (!returned) { resolve(res); returned = true; } } catch (e) { reject(e); } }); execObj.finally(() => { cce.release(); }); return execObj; } export function check__(cce: CallContextEvent, ...args): any { return getRuntime().scopedExec( cce, 'check', { data: {} }, async (resolve, reject, scopeContext) => { const resObj = grabActualResult(await punchGrab(args[0])); const checkedRes = getRuntime().__check(resObj, cce.spot, cce.blockContext, cce.scopeContext, 'check'); if (checkedRes instanceof Error) { return reject(checkedRes); } else { return resolve(checkedRes); } }); } export function sleep__(cce: CallContextEvent, duration: number, unit: TimeUnit = 's'): any { if (!unit) { unit = 's'; } let ms = unitTimeMs(duration, unit); if (ms < 0) { ms = 0; } return promise(async resolve => duration > 0 ? setTimeout(resolve, ms) : setImmediate(resolve)); } export function msleep__(cce: CallContextEvent, ms: number): any { if (ms < 0) { ms = 0; } return promise(async resolve => ms > 0 ? setTimeout(resolve, ms) : setImmediate(resolve)); } export namespace dsl__ { export let enabled = true; } /* eslint-disable @typescript-eslint/ban-types */ declare global { interface Object { $: Object & number; _: any; __: PostfixReturn; must: typeof must; should: typeof should; contains: (element: any) => boolean; containsEquivalent: (element: any) => boolean; equivalentTo: (target: any) => boolean; } interface Array { $: Array & number; _: any; __: PostfixReturn>; must: typeof must; should: typeof should; contains: (element: T) => boolean; containsEquivalent: (element: T) => boolean; /** * **DSL_ON** * */ equivalentTo: (target: any) => boolean; get first(): T; get last(): T; } interface String { $: String & number; _: any; __: PostfixReturn; must: typeof must; should: typeof should; contains: (subpattern: string | Buffer) => boolean; dedent: () => string; toBase64: (encoding?: BufferEncoding) => string; toHex: (encoding?: BufferEncoding) => string; toBuffer: (encoding?: BufferEncoding) => Buffer; } interface Boolean { $: Boolean & number; _: any; __: PostfixReturn; must: typeof must; should: typeof should; toNumber: () => number; } interface Number { $: Number & number; _: any; __: PostfixReturn; must: typeof must; should: typeof should; toNumber: () => number; within: (n: number) => boolean; } interface BigInt { $: BigInt & number; _: any; __: PostfixReturn; must: typeof must; should: typeof should; toNumber: () => number; within: (n: bigint) => boolean; } interface Symbol { $: Symbol & number; _: any; __: PostfixReturn; must: typeof must; should: typeof should; } interface Buffer { $: Buffer & number; _: any; __: PostfixReturn; must: typeof must; should: typeof should; contains: (subpattern: string | Buffer) => boolean; equivalentTo: (target: any) => boolean; toBase64: () => string; toHex: () => string; } const __context: __ScopeContext; const __block: __BlockContext; const __function: __FunctionContext; const __runtime: __RuntimeContext; // const __statement: any; // const __last_return: any; const $r: any; } import 'reflect-metadata';