@fabriktor/fx 0.0.139 → 0.0.141

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.
Files changed (37) hide show
  1. package/dist/src/chat/data.d.ts +23 -0
  2. package/dist/src/chat/data.js +76 -0
  3. package/dist/src/core/address.d.ts +2 -0
  4. package/dist/src/core/address.js +6 -0
  5. package/dist/src/core/date.d.ts +6 -0
  6. package/dist/src/core/date.js +27 -0
  7. package/dist/src/core/measurement.d.ts +15 -0
  8. package/dist/src/core/measurement.js +45 -0
  9. package/dist/src/core/server-clock.d.ts +10 -0
  10. package/dist/src/core/server-clock.js +36 -0
  11. package/dist/src/fx.d.ts +5 -1
  12. package/dist/src/fx.js +6 -1
  13. package/dist/src/index.d.ts +12 -0
  14. package/dist/src/index.js +12 -0
  15. package/dist/src/routes/arrival.d.ts +16 -0
  16. package/dist/src/routes/arrival.js +50 -0
  17. package/dist/src/routes/availability.d.ts +35 -0
  18. package/dist/src/routes/availability.js +132 -0
  19. package/dist/src/routes/departure.d.ts +45 -0
  20. package/dist/src/routes/departure.js +98 -0
  21. package/dist/src/routes/planning.d.ts +34 -0
  22. package/dist/src/routes/planning.js +94 -0
  23. package/dist/src/routes/punch.d.ts +9 -0
  24. package/dist/src/routes/punch.js +23 -0
  25. package/dist/src/routes/routes.d.ts +45 -12
  26. package/dist/src/routes/routes.js +223 -139
  27. package/dist/src/routes/tracking.d.ts +59 -0
  28. package/dist/src/routes/tracking.js +178 -0
  29. package/dist/src/routes/validation.d.ts +13 -0
  30. package/dist/src/routes/validation.js +227 -0
  31. package/dist/src/routes/workspace.d.ts +74 -0
  32. package/dist/src/routes/workspace.js +212 -0
  33. package/dist/src/timesheets/timesheets.d.ts +3 -1
  34. package/dist/src/timesheets/timesheets.js +3 -0
  35. package/dist/src/timesheets/workspace.d.ts +9 -1
  36. package/dist/src/timesheets/workspace.js +50 -8
  37. package/package.json +4 -3
@@ -0,0 +1,212 @@
1
+ // Copyright (C) Fabriktor, Inc. 2025-present.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+ // not use this file except in compliance with the License. You may obtain
5
+ // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ import { queryNestedParam, queryParams } from "@fabriktor/client";
7
+ import { ColRoutesClientAvailabilities, ColRoutesRouteTeams, ColRoutesRoutes, ColRoutesTags, ColRoutesTracking, UserTypeClient, UserTypeEmployee, UserTypeEmployer, } from "@fabriktor/schema";
8
+ import { isValidInputDate } from "../core/date.js";
9
+ import { errResolveFailed, errValidation } from "../core/err.js";
10
+ import { normalizeIDs, resolvedID } from "../core/id.js";
11
+ import { routeInput, validateRouteInput, validateTrackingInput, } from "./validation.js";
12
+ export const routeRefreshDefaults = {
13
+ delay_ms: 30_000,
14
+ max_attempts: 120,
15
+ timeout_ms: 15_000,
16
+ };
17
+ export const RoutesResource = {
18
+ Routes: ColRoutesRoutes,
19
+ Tags: ColRoutesTags,
20
+ Teams: ColRoutesRouteTeams,
21
+ ClientAvailabilities: ColRoutesClientAvailabilities,
22
+ Tracking: ColRoutesTracking,
23
+ };
24
+ // Only tags and teams expose Atlas search. These are UI capabilities, never
25
+ // a substitute for the service's per-record authorization.
26
+ export const routesSearchableResources = [
27
+ RoutesResource.Tags,
28
+ RoutesResource.Teams,
29
+ ];
30
+ const msgAccess = "This Routes operation is not available for the current actor.";
31
+ const msgOwner = "An explicit employer scope is required.";
32
+ const msgDate = "A valid date with an explicit operational timezone is required.";
33
+ const msgDaysAgo = "days_ago must be a nonnegative whole number.";
34
+ const msgEmployees = "At least one employee must be supplied for availability.";
35
+ const msgAvailabilityMissing = "The availability response did not contain a value.";
36
+ const msgDesignationMissing = "The designation response did not contain a value.";
37
+ const msgAssignment = "Tracking requires an assigned employee and a vehicle with scheduled stops.";
38
+ export function routeCanPlan(actor) {
39
+ return !!resolvedID(actor.owner_id) && actor.active_mode === UserTypeEmployer;
40
+ }
41
+ export function routeCanManage(actor, route) {
42
+ return routeCanPlan(actor) && actor.owner_id === route.owner_id;
43
+ }
44
+ export function routeCanRead(actor, route) {
45
+ if (!resolvedID(actor.owner_id))
46
+ return false;
47
+ if (routeCanManage(actor, route))
48
+ return true;
49
+ return ((actor.active_mode === UserTypeEmployee &&
50
+ route.utilized_employee_ids.includes(actor.owner_id)) ||
51
+ (actor.active_mode === UserTypeClient &&
52
+ route.utilized_client_ids.includes(actor.owner_id)));
53
+ }
54
+ export function routeTrackingVehicle(route, vehicle_id, employee_id) {
55
+ if (route.override_itinerary)
56
+ return undefined;
57
+ return route.itinerary.routes.find((vehicle) => vehicle.vehicle_id === vehicle_id &&
58
+ !vehicle.is_omitted &&
59
+ vehicle.stops.length > 0 &&
60
+ vehicle.employee_ids.includes(employee_id));
61
+ }
62
+ export function routeCanTrack(actor, route, vehicle_id, employee_id) {
63
+ return (!!resolvedID(actor.owner_id) &&
64
+ !!routeTrackingVehicle(route, vehicle_id, employee_id) &&
65
+ (routeCanManage(actor, route) ||
66
+ (actor.active_mode === UserTypeEmployee &&
67
+ actor.owner_id === employee_id)));
68
+ }
69
+ export function routeTrackingInput(actor, route, vehicle_id, employee_id, position) {
70
+ requireRoutes(routeCanTrack(actor, route, vehicle_id, employee_id), msgAssignment);
71
+ const input = {
72
+ ...position,
73
+ timestamp: { ...position.timestamp },
74
+ route_id: route.id,
75
+ vehicle_id,
76
+ employee_id,
77
+ };
78
+ validateTrackingInput(input);
79
+ return input;
80
+ }
81
+ export class RoutesWorkspace {
82
+ routes;
83
+ constructor(routes) {
84
+ this.routes = routes;
85
+ }
86
+ async readRoutes(opts) {
87
+ const actor = opts.actor;
88
+ const owner_id = routeReadOwner(actor, opts.employer_id);
89
+ if (opts.day)
90
+ requireRoutes(isValidInputDate(opts.day), msgDate);
91
+ const query = {
92
+ ...queryParams({
93
+ owner_id,
94
+ // A day filter refers to the calendar day stored on each route.
95
+ // Employers may operate depots in different timezones.
96
+ utilized_employee_ids: actor.active_mode === UserTypeEmployee
97
+ ? actor.owner_id
98
+ : optionalIDs(opts.employee_ids),
99
+ utilized_client_ids: actor.active_mode === UserTypeClient
100
+ ? actor.owner_id
101
+ : optionalIDs(opts.client_ids),
102
+ }),
103
+ ...(opts.day
104
+ ? {
105
+ ...queryNestedParam(["from", "y"], opts.day.y),
106
+ ...queryNestedParam(["from", "m"], opts.day.m),
107
+ ...queryNestedParam(["from", "d"], opts.day.d),
108
+ }
109
+ : {}),
110
+ };
111
+ const out = await this.routes.queryRoutes({ query, signal: opts.signal });
112
+ // Preserve backend membership/order; never emulate a collection search.
113
+ return out.value ?? [];
114
+ }
115
+ async availability(opts) {
116
+ requireRoutes(routeCanPlan(opts.actor), msgAccess);
117
+ requireRoutes(isValidInputDate(opts.date), msgDate);
118
+ requireRoutes(Number.isSafeInteger(opts.days_ago) && opts.days_ago >= 0, msgDaysAgo);
119
+ const employee_ids = normalizeIDs(opts.employee_ids);
120
+ requireRoutes(employee_ids.length > 0, msgEmployees);
121
+ const out = await this.routes.getAvailability({ employee_ids, days_ago: opts.days_ago, date: opts.date }, opts.request);
122
+ if (out.value === undefined)
123
+ throw errResolveFailed({
124
+ resource: RoutesResource.ClientAvailabilities,
125
+ msg: msgAvailabilityMissing,
126
+ });
127
+ return out.value;
128
+ }
129
+ async prepareRoute(opts) {
130
+ requireRoutes(routeCanPlan(opts.actor), msgAccess);
131
+ const input = routeInput(opts.in);
132
+ validateRouteInput(input);
133
+ const availability = await this.availability({
134
+ actor: opts.actor,
135
+ employee_ids: input.vehicles.flatMap((vehicle) => vehicle.employee_ids),
136
+ days_ago: opts.days_ago,
137
+ date: input.from,
138
+ request: opts.request,
139
+ });
140
+ validateRouteInput(input, availability);
141
+ return { in: input, availability };
142
+ }
143
+ async designations(opts) {
144
+ requireRoutes(!!resolvedID(opts.actor.owner_id) &&
145
+ (opts.actor.active_mode === UserTypeEmployee ||
146
+ opts.actor.active_mode === UserTypeClient), msgAccess);
147
+ const employer_id = resolvedID(opts.employer_id);
148
+ requireRoutes(employer_id, msgOwner);
149
+ requireRoutes(isValidInputDate(opts.date), msgDate);
150
+ // The server derives the viewer from authentication. There is no viewer
151
+ // ID override in this payload; date means this day onward, not one day.
152
+ const out = await this.routes.getDesignation({ employer_id, date: opts.date }, opts.request);
153
+ if (out.value === undefined)
154
+ throw errResolveFailed({
155
+ resource: RoutesResource.Routes,
156
+ msg: msgDesignationMissing,
157
+ });
158
+ return out.value;
159
+ }
160
+ async clientAvailabilities(opts) {
161
+ requireRoutes(!!resolvedID(opts.actor.owner_id) &&
162
+ opts.actor.active_mode === UserTypeClient, msgAccess);
163
+ const out = await this.routes.queryClientAvailabilities({
164
+ signal: opts.signal,
165
+ query: queryParams({
166
+ owner_id: opts.actor.owner_id,
167
+ employer_id: optionalIDs(opts.employer_ids),
168
+ location_id: optionalIDs(opts.location_ids),
169
+ timezone: opts.timezone,
170
+ }),
171
+ });
172
+ return out.value ?? [];
173
+ }
174
+ async tracking(opts) {
175
+ requireRoutes(routeCanRead(opts.actor, opts.route) &&
176
+ opts.actor.active_mode !== UserTypeClient, msgAccess);
177
+ const out = await this.routes.queryTracking({
178
+ signal: opts.signal,
179
+ query: queryParams({
180
+ owner_id: opts.route.owner_id,
181
+ route_id: opts.route.id,
182
+ employee_id: opts.actor.active_mode === UserTypeEmployee
183
+ ? opts.actor.owner_id
184
+ : optionalIDs(opts.employee_ids),
185
+ vehicle_id: optionalIDs(opts.vehicle_ids),
186
+ }),
187
+ });
188
+ return out.value ?? [];
189
+ }
190
+ }
191
+ function routeReadOwner(actor, employer_id) {
192
+ requireRoutes(resolvedID(actor.owner_id), msgAccess);
193
+ if (routeCanPlan(actor)) {
194
+ requireRoutes(employer_id === undefined || resolvedID(employer_id) === actor.owner_id, msgAccess);
195
+ return actor.owner_id;
196
+ }
197
+ requireRoutes(actor.active_mode === UserTypeEmployee ||
198
+ actor.active_mode === UserTypeClient, msgAccess);
199
+ const owner_id = resolvedID(employer_id);
200
+ requireRoutes(owner_id, msgOwner);
201
+ return owner_id;
202
+ }
203
+ function optionalIDs(values) {
204
+ if (values === undefined)
205
+ return undefined;
206
+ const ids = normalizeIDs(values);
207
+ return ids.length ? ids : undefined;
208
+ }
209
+ function requireRoutes(value, msg) {
210
+ if (!value)
211
+ throw errValidation({ msg });
212
+ }
@@ -1,6 +1,7 @@
1
1
  import { type Date as SchemaDate, type OperationResult, type Summary, type WeekSheet } from "@fabriktor/schema";
2
2
  import type { BillableOperator, TimesheetsOperator } from "@fabriktor/client";
3
3
  import type { ContactsFXOperator } from "../contacts/contacts.js";
4
+ import type { NowProvider } from "../core/server-clock.js";
4
5
  import type { MultiCreateAttempt } from "../core/multi-create.js";
5
6
  import { type LoadTimesheetsReadContextOptions, type QueryTimesheetSyncDestinationsOptions, type QueryTimesheetSyncSourcesOptions, type TimesheetsReadContext, type TimesheetsReadOptions, type TimesheetSyncTarget } from "./access.js";
6
7
  import { TimesheetsWorkspace } from "./workspace.js";
@@ -13,7 +14,7 @@ export type TimesheetsFXOptions = {
13
14
  timesheets: TimesheetsOperator;
14
15
  contacts_fx: ContactsFXOperator;
15
16
  billable?: BillableOperator;
16
- now?: () => globalThis.Date;
17
+ now?: NowProvider;
17
18
  };
18
19
  export type PunchContext = Pick<PunchPayload, "timezone" | "location_id" | "employer_id">;
19
20
  export type PunchInOptions = PunchContext & {
@@ -55,6 +56,7 @@ export type PunchControls = {
55
56
  can_punch_out: boolean;
56
57
  can_ride_in: boolean;
57
58
  can_ride_out: boolean;
59
+ can_start_transport: boolean;
58
60
  };
59
61
  export type SyncWeekOptions = {
60
62
  from: WeekSheet;
@@ -129,6 +129,7 @@ export class TimesheetsFX {
129
129
  can_punch_out: true,
130
130
  can_ride_in: !ride_active,
131
131
  can_ride_out: ride_active,
132
+ can_start_transport: !ride_active,
132
133
  };
133
134
  }
134
135
  async punchIn(opts) {
@@ -454,6 +455,7 @@ function disabledPunchControls() {
454
455
  can_punch_out: false,
455
456
  can_ride_in: false,
456
457
  can_ride_out: false,
458
+ can_start_transport: false,
457
459
  };
458
460
  }
459
461
  function readyToPunchInControls() {
@@ -462,6 +464,7 @@ function readyToPunchInControls() {
462
464
  can_punch_out: false,
463
465
  can_ride_in: false,
464
466
  can_ride_out: false,
467
+ can_start_transport: true,
465
468
  };
466
469
  }
467
470
  function cloneDate(v) {
@@ -1,5 +1,6 @@
1
1
  import { type BillableOperator, type TimesheetsOperator } from "@fabriktor/client";
2
2
  import { PunchTypeIn, PunchTypeOut, PunchTypeRideIn, PunchTypeRideOut, type InDate, type Punch, type Summary, type WeekSheet } from "@fabriktor/schema";
3
+ import type { NowProvider } from "../core/server-clock.js";
3
4
  import type { TimesheetsFXOperator } from "./timesheets.js";
4
5
  export type TimesheetFilters = {
5
6
  owner_ids?: WeekSheet["owner_id"][];
@@ -24,12 +25,14 @@ export type TimesheetPunchState = {
24
25
  };
25
26
  export type TimesheetPunchAction = typeof PunchTypeIn | typeof PunchTypeOut | typeof PunchTypeRideIn | typeof PunchTypeRideOut;
26
27
  export type CheckedPunchOptions = PunchStateOptions & {
28
+ before_request?: () => void;
27
29
  location_id: string;
28
30
  action: TimesheetPunchAction;
29
31
  expected: TimesheetPunchState;
30
32
  total_pause_minutes?: number;
31
33
  total_meal_minutes?: number;
32
34
  };
35
+ export type StartTimesheetTransportOptions = Omit<CheckedPunchOptions, "action" | "total_pause_minutes" | "total_meal_minutes">;
33
36
  export type SaveTimesheetDayOptions = PunchStateOptions & {
34
37
  location_id: string;
35
38
  start: InDate;
@@ -39,6 +42,7 @@ export type SaveTimesheetDayOptions = PunchStateOptions & {
39
42
  replace?: WeekSheet;
40
43
  };
41
44
  export declare const TimesheetsBlock: {
45
+ readonly Busy: "timesheets_busy";
42
46
  readonly Changed: "timesheets_changed";
43
47
  readonly Locked: "timesheets_locked";
44
48
  readonly Active: "timesheets_active";
@@ -59,7 +63,8 @@ export declare class TimesheetsWorkspace {
59
63
  private readonly actions;
60
64
  private readonly billable?;
61
65
  private readonly now;
62
- constructor(client: TimesheetsOperator, actions: TimesheetsFXOperator, billable?: BillableOperator | undefined, now?: () => Date);
66
+ private punchPending;
67
+ constructor(client: TimesheetsOperator, actions: TimesheetsFXOperator, billable?: BillableOperator | undefined, now?: NowProvider);
63
68
  weeks(opts: TimesheetFilters): Promise<WeekSheet[]>;
64
69
  summaries(opts: TimesheetFilters): Promise<Summary[]>;
65
70
  week(id: string, signal?: AbortSignal): Promise<WeekSheet>;
@@ -67,6 +72,9 @@ export declare class TimesheetsWorkspace {
67
72
  periods(employer_id: string, start: InDate, end?: InDate): Promise<number[]>;
68
73
  punchState(opts: PunchStateOptions): Promise<TimesheetPunchState>;
69
74
  punch(opts: CheckedPunchOptions): Promise<WeekSheet>;
75
+ startTransport(opts: StartTimesheetTransportOptions): Promise<WeekSheet>;
76
+ private withPunch;
77
+ private performPunch;
70
78
  saveDay(opts: SaveTimesheetDayOptions): Promise<WeekSheet>;
71
79
  removeDay(owner_id: string, expected: WeekSheet, date: InDate): Promise<void>;
72
80
  removeWeek(actor_id: string, expected: WeekSheet): Promise<void>;
@@ -5,6 +5,7 @@ import { errRequired } from "../core/err.js";
5
5
  import { PreActionBlocked } from "../core/pre-action.js";
6
6
  import { queryRecordParams, runOwnerScopeQuery, } from "../core/query.js";
7
7
  export const TimesheetsBlock = {
8
+ Busy: "timesheets_busy",
8
9
  Changed: "timesheets_changed",
9
10
  Locked: "timesheets_locked",
10
11
  Active: "timesheets_active",
@@ -74,6 +75,7 @@ export class TimesheetsWorkspace {
74
75
  actions;
75
76
  billable;
76
77
  now;
78
+ punchPending = false;
77
79
  constructor(client, actions, billable, now = () => new Date()) {
78
80
  this.client = client;
79
81
  this.actions = actions;
@@ -109,7 +111,7 @@ export class TimesheetsWorkspace {
109
111
  }
110
112
  async punchState(opts) {
111
113
  requireState(!!opts.owner_id && !!opts.timezone, TimesheetsBlock.InvalidDay);
112
- const today = opts.date ?? inDateAtTimestamp(this.now().toISOString(), opts.timezone);
114
+ const today = opts.date ?? inDateAtTimestamp((await this.now()).toISOString(), opts.timezone);
113
115
  const week_day = timesheetWeekDay(today);
114
116
  // Check every open work punch, including other employers/locations/weeks.
115
117
  // A scope selection must never hide an already open shift.
@@ -124,7 +126,7 @@ export class TimesheetsWorkspace {
124
126
  requireState(open.length <= 1, TimesheetsBlock.Active);
125
127
  if (open[0]) {
126
128
  const current = open[0];
127
- const date = inDateAtTimestamp(this.now().toISOString(), current.punch.timezone);
129
+ const date = inDateAtTimestamp((await this.now()).toISOString(), current.punch.timezone);
128
130
  return {
129
131
  ...current,
130
132
  stale_day: !sameDay(current.punch.punch_in_time, opts.date ?? date),
@@ -146,8 +148,47 @@ export class TimesheetsWorkspace {
146
148
  return { week_sheet, punch, week_day, stale_day: false };
147
149
  }
148
150
  async punch(opts) {
151
+ return await this.withPunch(() => this.performPunch(opts));
152
+ }
153
+ // A trip belongs to its destination's work session. Keep both steps ordered
154
+ // and validate the acknowledged work state before starting its transport.
155
+ // Never retry or roll back a punch automatically after an uncertain response.
156
+ async startTransport(opts) {
157
+ return await this.withPunch(async () => {
158
+ let expected = opts.expected;
159
+ if (expected.punch?.punch_status !== PunchStatusWorking) {
160
+ const week_sheet = await this.performPunch({
161
+ ...opts,
162
+ action: PunchTypeIn,
163
+ });
164
+ const week_day = expected.week_day;
165
+ const punch = week_sheet.sheets
166
+ .find((day) => day.week_day === week_day)
167
+ ?.punches.at(-1);
168
+ expected = { week_sheet, punch, week_day, stale_day: false };
169
+ }
170
+ return await this.performPunch({
171
+ ...opts,
172
+ expected,
173
+ action: PunchTypeRideIn,
174
+ });
175
+ });
176
+ }
177
+ // The existing FX request guard protects individual writes. This guard also
178
+ // keeps other checked punch actions out of the gap between these two steps.
179
+ async withPunch(fn) {
180
+ requireState(!this.punchPending, TimesheetsBlock.Busy);
181
+ this.punchPending = true;
182
+ try {
183
+ return await fn();
184
+ }
185
+ finally {
186
+ this.punchPending = false;
187
+ }
188
+ }
189
+ async performPunch(opts) {
149
190
  if (opts.date)
150
- this.validateDay(opts.date, opts.timezone);
191
+ await this.validateDay(opts.date, opts.timezone);
151
192
  const current = await this.punchState(opts);
152
193
  requireState(timesheetStateKey(current) === timesheetStateKey(opts.expected), TimesheetsBlock.Changed);
153
194
  requireState(!current.stale_day, TimesheetsBlock.InvalidDay);
@@ -169,6 +210,7 @@ export class TimesheetsWorkspace {
169
210
  requireState(sameDay(opts.date, current.punch.punch_in_time) &&
170
211
  wallTime(opts.date) >= wallTime(current.punch.punch_in_time), TimesheetsBlock.InvalidDay);
171
212
  }
213
+ opts.before_request?.();
172
214
  const context = {
173
215
  employer_id: opts.employer_id,
174
216
  location_id: opts.location_id,
@@ -204,8 +246,8 @@ export class TimesheetsWorkspace {
204
246
  return sheet;
205
247
  }
206
248
  async saveDay(opts) {
207
- this.validateDay(opts.start, opts.timezone);
208
- this.validateDay(opts.end, opts.timezone);
249
+ await this.validateDay(opts.start, opts.timezone);
250
+ await this.validateDay(opts.end, opts.timezone);
209
251
  requireState(sameDay(opts.start, opts.end) &&
210
252
  wallTime(opts.end) > wallTime(opts.start), TimesheetsBlock.InvalidDay);
211
253
  const indexes = await this.periods(opts.employer_id, opts.start);
@@ -267,7 +309,7 @@ export class TimesheetsWorkspace {
267
309
  await this.client.deleteOneWeekSheet({ id: sheet.id });
268
310
  }
269
311
  async blankWeek(opts) {
270
- this.validateDay(opts.date, opts.timezone);
312
+ await this.validateDay(opts.date, opts.timezone);
271
313
  const indexes = await this.periods(opts.employer_id, opts.date);
272
314
  const matches = await this.weeks({
273
315
  owner_ids: [opts.owner_id],
@@ -304,8 +346,8 @@ export class TimesheetsWorkspace {
304
346
  requireState(timesheetIsMutable(sheet), TimesheetsBlock.Locked);
305
347
  return sheet;
306
348
  }
307
- validateDay(date, timezone) {
308
- const now = inDateAtTimestamp(this.now().toISOString(), timezone);
349
+ async validateDay(date, timezone) {
350
+ const now = inDateAtTimestamp((await this.now()).toISOString(), timezone);
309
351
  const calendar = new Date(Date.UTC(date.y, date.m - 1, date.d));
310
352
  requireState(!!now &&
311
353
  date.tz === timezone &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabriktor/fx",
3
- "version": "0.0.139",
3
+ "version": "0.0.141",
4
4
  "description": "![Fabriktor Logo](./img/fabriktor-character.gif)",
5
5
  "type": "module",
6
6
  "exports": {
@@ -17,8 +17,9 @@
17
17
  "typescript": "^6.0.3"
18
18
  },
19
19
  "dependencies": {
20
- "@fabriktor/client": "0.0.97",
20
+ "@fabriktor/client": "0.0.98",
21
21
  "@fabriktor/fx": "0.0.94",
22
- "@fabriktor/schema": "0.0.68"
22
+ "@fabriktor/schema": "0.0.68",
23
+ "convert": "8.0.2"
23
24
  }
24
25
  }