bazis 0.133.0 → 0.134.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/core/agent/AgentToolExecutor.ts +5 -0
- package/core/agent/Tool.contract.ts +2 -2
- package/core/agent/metadata.ts +11 -0
- package/core/infra/connectors/codex/CodexAppServerClient.ts +1 -1
- package/core/scripts/di-generate.ts +8 -0
- package/core/tools/StandardTools.module.ts +14 -0
- package/core/tools/generated/deps.ts +10 -0
- package/core/tools/index.ts +8 -0
- package/core/tools/orm/OrmEntity.references.ts +71 -0
- package/core/tools/orm/ormEntityTools.ts +423 -0
- package/core/workflow/contracts/WorkflowError.ts +2 -1
- package/core/workflow/contracts/WorkflowPartitions.ts +29 -0
- package/core/workflow/contracts/WorkflowProcessUsage.ts +8 -0
- package/core/workflow/definitions_module/WorkflowDefinitions.module.ts +7 -2
- package/core/workflow/definitions_module/access/Definitions.permissions.ts +4 -2
- package/core/workflow/definitions_module/access/ProcessPartition.lookup.ts +27 -0
- package/core/workflow/definitions_module/events/Definitions.events.ts +1 -1
- package/core/workflow/definitions_module/http/Processes.controller.ts +19 -4
- package/core/workflow/definitions_module/services/IWorkflowDefinitions.service.ts +8 -0
- package/core/workflow/definitions_module/services/WorkflowDefinitions.service.ts +24 -0
- package/core/workflow/generated/deps.ts +8 -2
- package/core/workflow/index.ts +5 -0
- package/core/workflow/runtime_module/WorkflowRuntime.module.ts +5 -0
- package/core/workflow/runtime_module/internal/EventDelivery.ts +234 -0
- package/core/workflow/runtime_module/internal/RunLimits.ts +13 -3
- package/core/workflow/runtime_module/internal/RuntimeProcessUsage.ts +13 -0
- package/core/workflow/runtime_module/internal/Schedules.ts +97 -30
- package/core/workflow/runtime_module/internal/WorkflowEngine.ts +29 -3
- package/core/workflow/runtime_module/internal/WorkflowJobs.ts +7 -0
- package/core/workflow/runtime_module/model/WfJob.model.ts +7 -6
- package/core/workflow/runtime_module/services/IWorkflowRuntime.service.ts +21 -1
- package/core/workflow/runtime_module/services/WorkflowRuntime.service.ts +29 -40
- package/library/workflow/cron.ts +103 -24
- package/library/workflow/index.ts +2 -2
- package/library/workflow/nodeKinds.ts +64 -9
- package/library/workflow/paths.ts +2 -2
- package/library/workflow/validateProcess.ts +12 -0
- package/package.json +2 -1
package/library/workflow/cron.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** A parsed five-field cron expression, evaluated in UTC. */
|
|
1
|
+
/** A parsed five-field cron expression, evaluated in UTC or in a time zone. */
|
|
2
2
|
export interface CronSchedule {
|
|
3
3
|
/** Whether the expression fires at this minute. */
|
|
4
4
|
matches(date: Date): boolean;
|
|
@@ -15,14 +15,53 @@ const FIELDS = [
|
|
|
15
15
|
] as const;
|
|
16
16
|
|
|
17
17
|
const MINUTE = 60_000;
|
|
18
|
+
const DAY_MINUTES = 24 * 60;
|
|
19
|
+
/** Clock changes are never longer than this: a day is skipped up to here, the rest hour by hour. */
|
|
20
|
+
const CLOCK_CHANGE_MINUTES = 3 * 60;
|
|
21
|
+
|
|
22
|
+
const formats = new Map<string, Intl.DateTimeFormat>();
|
|
23
|
+
|
|
24
|
+
function formatOf(timezone: string): Intl.DateTimeFormat {
|
|
25
|
+
let format = formats.get(timezone);
|
|
26
|
+
if (format === undefined) {
|
|
27
|
+
format = new Intl.DateTimeFormat("en-US", { timeZone: timezone, hourCycle: "h23", year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric" });
|
|
28
|
+
formats.set(timezone, format);
|
|
29
|
+
}
|
|
30
|
+
return format;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Whether this is an IANA time zone: `Europe/Moscow`, `UTC`. */
|
|
34
|
+
export function isTimeZone(value: unknown): value is string {
|
|
35
|
+
if (typeof value !== "string" || value === "") return false;
|
|
36
|
+
try {
|
|
37
|
+
formatOf(value);
|
|
38
|
+
return true;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** How far the clocks of the zone are ahead of UTC at this moment, in milliseconds. */
|
|
45
|
+
function offsetOf(timezone: string | undefined, time: number): number {
|
|
46
|
+
if (timezone === undefined) return 0;
|
|
47
|
+
const parts: Record<string, number> = {};
|
|
48
|
+
for (const { type, value } of formatOf(timezone).formatToParts(time)) parts[type] = Number(value);
|
|
49
|
+
return Date.UTC(parts.year!, parts.month! - 1, parts.day!, parts.hour!, parts.minute!, parts.second!) - Math.floor(time / 1000) * 1000;
|
|
50
|
+
}
|
|
18
51
|
|
|
19
52
|
/**
|
|
20
53
|
* Parses `minute hour day-of-month month day-of-week` with `*`, numbers,
|
|
21
54
|
* ranges `a-b`, steps `/n` and lists `a,b`. Day of week 0 and 7 are Sunday.
|
|
22
55
|
* When both day fields are restricted, either one matching is enough, as in
|
|
23
|
-
* classic cron. Returns `undefined` for an invalid expression.
|
|
56
|
+
* classic cron. Returns `undefined` for an invalid expression or time zone.
|
|
57
|
+
*
|
|
58
|
+
* With a `timezone` the fields are read on its clocks. A schedule with a set
|
|
59
|
+
* hour fires once a day whatever the clocks do: a time skipped when they go
|
|
60
|
+
* forward fires right after the change, a time repeated when they go back fires
|
|
61
|
+
* the first time only. A schedule with `*` in the hour simply follows the clocks.
|
|
24
62
|
*/
|
|
25
|
-
export function parseCron(expression: string): CronSchedule | undefined {
|
|
63
|
+
export function parseCron(expression: string, timezone?: string): CronSchedule | undefined {
|
|
64
|
+
if (timezone !== undefined && !isTimeZone(timezone)) return undefined;
|
|
26
65
|
const parts = expression.trim().split(/\s+/);
|
|
27
66
|
if (parts.length !== 5) return undefined;
|
|
28
67
|
const sets: Set<number>[] = [];
|
|
@@ -35,34 +74,74 @@ export function parseCron(expression: string): CronSchedule | undefined {
|
|
|
35
74
|
if (weekdays.has(7)) weekdays.add(0);
|
|
36
75
|
const daysRestricted = parts[2] !== "*";
|
|
37
76
|
const weekdaysRestricted = parts[4] !== "*";
|
|
77
|
+
const hourSet = parts[1] !== "*";
|
|
38
78
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
79
|
+
/** The date part of a time on the clocks of the zone, kept in the UTC fields of a `Date`. */
|
|
80
|
+
const dayMatches = (clock: Date): boolean => {
|
|
81
|
+
const day = days.has(clock.getUTCDate());
|
|
82
|
+
const weekday = weekdays.has(clock.getUTCDay());
|
|
83
|
+
return months.has(clock.getUTCMonth() + 1) && (daysRestricted && weekdaysRestricted ? day || weekday : day && weekday);
|
|
43
84
|
};
|
|
44
85
|
|
|
45
|
-
const
|
|
46
|
-
|
|
86
|
+
const clockMatches = (clock: Date): boolean => minutes.has(clock.getUTCMinutes()) && hours.has(clock.getUTCHours()) && dayMatches(clock);
|
|
87
|
+
|
|
88
|
+
/** Whether the clocks show this time for the second time: they went back a moment ago. */
|
|
89
|
+
const repeated = (time: number, offset: number): boolean => {
|
|
90
|
+
const before = offsetOf(timezone, time - CLOCK_CHANGE_MINUTES * MINUTE);
|
|
91
|
+
if (before <= offset) return false;
|
|
92
|
+
const first = time - (before - offset);
|
|
93
|
+
return offsetOf(timezone, first) === before;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/** When the clocks jumped forward between two moments, if a firing fell into the skipped time. */
|
|
97
|
+
const skippedFiring = (from: number, to: number): number | undefined => {
|
|
98
|
+
const before = offsetOf(timezone, from);
|
|
99
|
+
const after = offsetOf(timezone, to);
|
|
100
|
+
if (after <= before) return undefined;
|
|
101
|
+
let low = from;
|
|
102
|
+
let high = to;
|
|
103
|
+
while (high - low > MINUTE) {
|
|
104
|
+
const middle = Math.floor((low + high) / 2 / MINUTE) * MINUTE;
|
|
105
|
+
if (offsetOf(timezone, middle) === before) low = middle;
|
|
106
|
+
else high = middle;
|
|
107
|
+
}
|
|
108
|
+
for (let clock = high + before; clock < high + after; clock += MINUTE) {
|
|
109
|
+
if (clockMatches(new Date(clock))) return high;
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
};
|
|
47
113
|
|
|
48
114
|
return {
|
|
49
|
-
matches
|
|
115
|
+
matches(date: Date): boolean {
|
|
116
|
+
const offset = offsetOf(timezone, date.getTime());
|
|
117
|
+
if (!clockMatches(new Date(date.getTime() + offset))) return false;
|
|
118
|
+
return !(hourSet && timezone !== undefined && repeated(Math.floor(date.getTime() / MINUTE) * MINUTE, offset));
|
|
119
|
+
},
|
|
50
120
|
next(after: Date): Date | undefined {
|
|
51
|
-
const limit = after.getTime() + 5 * 366 *
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
121
|
+
const limit = after.getTime() + 5 * 366 * DAY_MINUTES * MINUTE;
|
|
122
|
+
let time = Math.floor(after.getTime() / MINUTE) * MINUTE + MINUTE;
|
|
123
|
+
let previous = time - MINUTE;
|
|
124
|
+
while (time <= limit) {
|
|
125
|
+
if (hourSet && timezone !== undefined) {
|
|
126
|
+
const skipped = skippedFiring(previous, time);
|
|
127
|
+
if (skipped !== undefined) return new Date(skipped);
|
|
128
|
+
}
|
|
129
|
+
previous = time;
|
|
130
|
+
const offset = offsetOf(timezone, time);
|
|
131
|
+
const clock = new Date(time + offset);
|
|
132
|
+
const minutesOfDay = clock.getUTCHours() * 60 + clock.getUTCMinutes();
|
|
133
|
+
if (!dayMatches(clock)) {
|
|
134
|
+
// Close to midnight hour by hour: the day may be shorter than its clocks say.
|
|
135
|
+
const left = DAY_MINUTES - minutesOfDay;
|
|
136
|
+
time += (timezone === undefined ? left : left > CLOCK_CHANGE_MINUTES ? left - CLOCK_CHANGE_MINUTES : 60 - clock.getUTCMinutes()) * MINUTE;
|
|
137
|
+
} else if (!hours.has(clock.getUTCHours())) {
|
|
138
|
+
time += (60 - clock.getUTCMinutes()) * MINUTE;
|
|
139
|
+
} else if (!minutes.has(clock.getUTCMinutes())) {
|
|
140
|
+
time += MINUTE;
|
|
141
|
+
} else if (hourSet && timezone !== undefined && repeated(time, offset)) {
|
|
142
|
+
time += MINUTE;
|
|
64
143
|
} else {
|
|
65
|
-
return
|
|
144
|
+
return new Date(time);
|
|
66
145
|
}
|
|
67
146
|
}
|
|
68
147
|
return undefined;
|
|
@@ -20,7 +20,7 @@ export {
|
|
|
20
20
|
type ProcessNode,
|
|
21
21
|
type ProcessSettings,
|
|
22
22
|
} from "./ProcessDocument";
|
|
23
|
-
export { NODE_KINDS, nodeKindOf, ISO_DURATION, MAX_ITERATIONS, MAX_LOOP_PARALLEL, PROCESS_KEY, type JsonSchema, type NodeKindDefinition, type NodeRole } from "./nodeKinds";
|
|
23
|
+
export { NODE_KINDS, nodeKindOf, ISO_DURATION, MAX_ITERATIONS, MAX_LOOP_PARALLEL, PROCESS_KEY, ISO_MOMENT, type JsonSchema, type NodeKindDefinition, type NodeRole } from "./nodeKinds";
|
|
24
24
|
export { PROCESS_LIMITS, validateProcess, hasErrors, type ProcessIssue, type ProcessIssueCode, type ValidateProcessOptions } from "./validateProcess";
|
|
25
25
|
export { DATA_PATH, DATA_PATHS, isPath, readPath, resolveInput, type InstanceData } from "./paths";
|
|
26
26
|
export {
|
|
@@ -42,7 +42,7 @@ export {
|
|
|
42
42
|
} from "./expressions";
|
|
43
43
|
export { ExpressionSyntaxError, parseExpression, printExpression } from "./expressionSyntax";
|
|
44
44
|
export { durationMs } from "./duration";
|
|
45
|
-
export { parseCron, type CronSchedule } from "./cron";
|
|
45
|
+
export { isTimeZone, parseCron, type CronSchedule } from "./cron";
|
|
46
46
|
export { processToMermaid } from "./mermaid";
|
|
47
47
|
export { PERSONAL_MASK, maskByMapping, maskBySchema, masksPersonal, personalPaths, personalVariables, readsPersonal } from "./masking";
|
|
48
48
|
export { allTexts, checkLocalizedText, textOf, type LocalizedText } from "./localized";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseCron } from "./cron";
|
|
1
|
+
import { isTimeZone, parseCron } from "./cron";
|
|
2
2
|
import { FIELD_NAME, checkFormSpec } from "./formFields";
|
|
3
3
|
import { DATA_PATH } from "./paths";
|
|
4
4
|
import type { NodeKind } from "./ProcessDocument";
|
|
@@ -52,11 +52,42 @@ function first(...checks: (string | null)[]): string | null {
|
|
|
52
52
|
return checks.find((check) => check !== null) ?? null;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
/** A moment with its zone: `2026-11-01T09:00:00+03:00`. */
|
|
56
|
+
export const ISO_MOMENT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
57
|
+
|
|
58
|
+
function moment(config: Record<string, unknown>, key: string): string | null {
|
|
59
|
+
const value = config[key];
|
|
60
|
+
if (value === undefined) return null;
|
|
61
|
+
return typeof value === "string" && ISO_MOMENT.test(value) && !Number.isNaN(Date.parse(value)) ? null : `config.${key} must be a moment with its zone: 2026-11-01T09:00:00+03:00`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A scheduled start: five cron fields (minute, hour, day of month, month, day of week), where and when it runs. */
|
|
65
|
+
function schedule(config: Record<string, unknown>): string | null {
|
|
66
|
+
const { cron, timezone, from, until, overlap, partitions } = config;
|
|
67
|
+
if (typeof cron !== "string") return "config.cron is required";
|
|
68
|
+
if (parseCron(cron) === undefined) return "config.cron must be five valid fields: minute hour day month weekday";
|
|
69
|
+
if (timezone !== undefined && !isTimeZone(timezone)) return "config.timezone must be an IANA time zone: Europe/Moscow";
|
|
70
|
+
const period = first(moment(config, "from"), moment(config, "until"), boolean(config, "catchUp"));
|
|
71
|
+
if (period !== null) return period;
|
|
72
|
+
if (typeof from === "string" && typeof until === "string" && Date.parse(from) >= Date.parse(until)) return "config.from must be before config.until";
|
|
73
|
+
if (overlap !== undefined && overlap !== "skip" && overlap !== "queue" && overlap !== "parallel") return "config.overlap must be skip, queue or parallel";
|
|
74
|
+
if (partitions !== undefined && partitions !== "none" && partitions !== "all") {
|
|
75
|
+
if (!Array.isArray(partitions) || partitions.length === 0 || partitions.length > 1000 || !partitions.every((item) => typeof item === "string" && item !== "" && item.length <= 200)) return "config.partitions must be none, all or a list of partitions";
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** An event start; its expressions are checked with the whole document. */
|
|
81
|
+
function eventStart(config: Record<string, unknown>): string | null {
|
|
82
|
+
const { debounce } = config;
|
|
83
|
+
if (debounce !== undefined) {
|
|
84
|
+
if (typeof debounce !== "object" || debounce === null || Array.isArray(debounce)) return "config.debounce must be an object";
|
|
85
|
+
const { key } = debounce as { key?: unknown };
|
|
86
|
+
if (key === undefined) return "config.debounce.key is required";
|
|
87
|
+
const problem = string(debounce as Record<string, unknown>, "window", ISO_DURATION);
|
|
88
|
+
if (problem !== null) return problem.replace("config.", "config.debounce.");
|
|
89
|
+
}
|
|
90
|
+
return first(string(config, "event", DOTTED_KEY), string(config, "delay", ISO_DURATION, false), boolean(config, "ignoreOwn"));
|
|
60
91
|
}
|
|
61
92
|
|
|
62
93
|
/** A timer: either `duration` (ISO 8601) or `at`, a path to a date in instance data. */
|
|
@@ -158,8 +189,32 @@ const fields = { type: "object", properties: { fields: { type: "array", items: {
|
|
|
158
189
|
|
|
159
190
|
export const NODE_KINDS: readonly NodeKindDefinition[] = Object.freeze([
|
|
160
191
|
{ kind: "manualStart", role: "start", check: none, config: object({}) },
|
|
161
|
-
{
|
|
162
|
-
|
|
192
|
+
{
|
|
193
|
+
kind: "scheduleStart",
|
|
194
|
+
role: "start",
|
|
195
|
+
check: schedule,
|
|
196
|
+
config: object({
|
|
197
|
+
cron: text,
|
|
198
|
+
timezone: { type: "string", description: "An IANA time zone; UTC by default" },
|
|
199
|
+
from: { type: "string", format: "date-time" },
|
|
200
|
+
until: { type: "string", format: "date-time" },
|
|
201
|
+
overlap: { enum: ["skip", "queue", "parallel"] },
|
|
202
|
+
catchUp: flag,
|
|
203
|
+
partitions: { description: "none, all or a list of partitions", oneOf: [{ enum: ["none", "all"] }, { type: "array", items: text }] },
|
|
204
|
+
}, ["cron"]),
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
kind: "eventStart",
|
|
208
|
+
role: "start",
|
|
209
|
+
check: eventStart,
|
|
210
|
+
config: object({
|
|
211
|
+
event: text,
|
|
212
|
+
filter: expression,
|
|
213
|
+
debounce: object({ key: expression, window: duration }, ["key", "window"]),
|
|
214
|
+
delay: duration,
|
|
215
|
+
ignoreOwn: flag,
|
|
216
|
+
}, ["event"]),
|
|
217
|
+
},
|
|
163
218
|
{
|
|
164
219
|
kind: "userTask",
|
|
165
220
|
role: "activity",
|
|
@@ -218,7 +273,7 @@ export const NODE_KINDS: readonly NodeKindDefinition[] = Object.freeze([
|
|
|
218
273
|
kind: "eventWait",
|
|
219
274
|
role: "activity",
|
|
220
275
|
check: (c) => first(string(c, "event", DOTTED_KEY), string(c, "correlation", DATA_PATH, false), string(c, "saveTo", FIELD_NAME, false)),
|
|
221
|
-
config: object({ event: text, correlation: dataPath, saveTo }, ["event"]),
|
|
276
|
+
config: object({ event: text, correlation: dataPath, saveTo, until: expression }, ["event"]),
|
|
222
277
|
},
|
|
223
278
|
{ kind: "exclusiveGateway", role: "gateway", check: none, config: object({}) },
|
|
224
279
|
{ kind: "parallelGateway", role: "gateway", check: none, config: object({}) },
|
|
@@ -10,7 +10,7 @@ export interface InstanceData {
|
|
|
10
10
|
readonly nodes: Readonly<Record<string, unknown>>;
|
|
11
11
|
/** The data of the event that started or continued the process. */
|
|
12
12
|
readonly event?: unknown;
|
|
13
|
-
/** How it was started: `{ via, at, actor, eventKey?, cause? }`. */
|
|
13
|
+
/** How it was started: `{ via, at, actor, eventKey?, cause?, scheduledAt? }`. */
|
|
14
14
|
readonly trigger?: unknown;
|
|
15
15
|
/** `{ id, processKey, version, partition, test }`. */
|
|
16
16
|
readonly run?: unknown;
|
|
@@ -31,7 +31,7 @@ export const DATA_PATHS: readonly { readonly path: string; readonly description:
|
|
|
31
31
|
{ path: "$vars.<name>", description: "A variable of the process" },
|
|
32
32
|
{ path: "$nodes.<node>.<field>", description: "The result of a passed step" },
|
|
33
33
|
{ path: "$event", description: "The data of the event that started or continued the process" },
|
|
34
|
-
{ path: "$trigger", description: "How it was started: { via, at, actor, eventKey?, cause? }" },
|
|
34
|
+
{ path: "$trigger", description: "How it was started: { via, at, actor, eventKey?, cause?, scheduledAt? }" },
|
|
35
35
|
{ path: "$run", description: "This run: { id, processKey, version, partition, test }" },
|
|
36
36
|
{ path: "$now", description: "The engine's time" },
|
|
37
37
|
{ path: "$item", description: "Inside a loop body: the element of this iteration" },
|
|
@@ -451,6 +451,18 @@ function configExpressionProblems(kind: string, config: Json, scope: ExpressionS
|
|
|
451
451
|
const problem = checkExpression(config.over, scope);
|
|
452
452
|
if (problem) out.push(`config.over: ${problem}`);
|
|
453
453
|
}
|
|
454
|
+
if (kind === "eventWait" && config.until !== undefined) {
|
|
455
|
+
const problem = checkExpression(config.until, scope);
|
|
456
|
+
if (problem) out.push(`config.until: ${problem}`);
|
|
457
|
+
}
|
|
458
|
+
if (kind === "eventStart") {
|
|
459
|
+
// Before an instance exists there is only the event: nothing to read in the input, variables and steps.
|
|
460
|
+
const before: ExpressionScope = { input: new Set(), vars: new Set(), nodes: new Set(), loop: false };
|
|
461
|
+
const filter = config.filter === undefined ? null : checkExpression(config.filter, before);
|
|
462
|
+
if (filter) out.push(`config.filter: ${filter}`);
|
|
463
|
+
const debounceKey = isObject(config.debounce) ? checkExpression(config.debounce.key, before) : null;
|
|
464
|
+
if (debounceKey) out.push(`config.debounce.key: ${debounceKey}`);
|
|
465
|
+
}
|
|
454
466
|
for (const key of ["at", "correlation"]) {
|
|
455
467
|
if (typeof config[key] === "string") {
|
|
456
468
|
const problem = checkPath(config[key], scope);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bazis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.134.0",
|
|
4
4
|
"description": "Modular backend framework for Bun in TypeScript: DI wired by codegen, HTTP, a PostgreSQL ORM, JWT, WebSocket, agents and a CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alexander Alenin",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"./core/kernel": "./core/kernel/index.ts",
|
|
45
45
|
"./core/orm": "./core/orm/index.ts",
|
|
46
46
|
"./core/testing": "./core/testing/index.ts",
|
|
47
|
+
"./core/tools": "./core/tools/index.ts",
|
|
47
48
|
"./core/websocket": "./core/websocket/index.ts",
|
|
48
49
|
"./core/workflow": "./core/workflow/index.ts",
|
|
49
50
|
"./core/workflow/testing": "./core/workflow/testing/index.ts",
|