@unchainedshop/core 3.1.11 → 3.1.15

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.
@@ -0,0 +1,30 @@
1
+ import { Work } from '@unchainedshop/core-worker';
2
+ export interface IWorker<P extends {
3
+ workerId?: string;
4
+ }> {
5
+ key: string;
6
+ label: string;
7
+ version: string;
8
+ type: string;
9
+ external: boolean;
10
+ getFloorDate: (date?: Date) => Date;
11
+ actions: (params: P, unchainedAPI: any) => {
12
+ autorescheduleTypes: (options: {
13
+ referenceDate: Date;
14
+ }) => Promise<(Work | null)[]>;
15
+ process: (options: {
16
+ maxWorkItemCount?: number;
17
+ referenceDate?: Date;
18
+ }) => Promise<void>;
19
+ reset: () => Promise<void> | void;
20
+ start: () => Promise<void> | void;
21
+ stop: () => Promise<void> | void;
22
+ };
23
+ }
24
+ interface WorkerParams {
25
+ workerId?: string;
26
+ worker: IWorker<any>;
27
+ }
28
+ export declare const BaseWorker: IWorker<WorkerParams>;
29
+ export {};
30
+ //# sourceMappingURL=BaseWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseWorker.d.ts","sourceRoot":"","sources":["../../src/directors/BaseWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAY,MAAM,4BAA4B,CAAC;AAG5D,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAElB,YAAY,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,KAAK,IAAI,CAAC;IAEpC,OAAO,EAAE,CACP,MAAM,EAAE,CAAC,EACT,YAAY,KAAA,KACT;QACH,mBAAmB,EAAE,CAAC,OAAO,EAAE;YAAE,aAAa,EAAE,IAAI,CAAA;SAAE,KAAK,OAAO,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;QACpF,OAAO,EAAE,CAAC,OAAO,EAAE;YAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,IAAI,CAAA;SAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QACzF,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAClC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAClC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAClC,CAAC;CACH;AAED,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;CACtB;AAED,eAAO,MAAM,UAAU,EAAE,OAAO,CAAC,YAAY,CAsF5C,CAAC"}
@@ -0,0 +1,79 @@
1
+ import later from '@breejs/later';
2
+ import { WorkerDirector } from './WorkerDirector.js';
3
+ export const BaseWorker = {
4
+ key: 'shop.unchained.worker.base',
5
+ label: 'Base worker. Do not use this directly.',
6
+ version: '1.0.0',
7
+ type: 'BASE',
8
+ external: false,
9
+ getFloorDate: (date = new Date()) => {
10
+ const floored = new Date(Math.floor(date.getTime() / 1000) * 1000);
11
+ return floored;
12
+ },
13
+ actions: ({ workerId }, unchainedAPI) => {
14
+ const workerActions = {
15
+ async start() {
16
+ throw new Error(`Not implemented on ${this.constructor.key}`);
17
+ },
18
+ async stop() {
19
+ throw new Error(`Not implemented on ${this.constructor.key}`);
20
+ },
21
+ reset: async (referenceDate = new Date()) => {
22
+ await unchainedAPI.modules.worker.markOldWorkAsFailed({
23
+ types: WorkerDirector.getActivePluginTypes({ external: false }),
24
+ worker: workerId,
25
+ referenceDate,
26
+ });
27
+ },
28
+ autorescheduleTypes: async ({ referenceDate }) => {
29
+ return Promise.all(WorkerDirector.getAutoSchedules().map(async ([scheduleId, workConfig]) => {
30
+ if (!workConfig.schedule) {
31
+ await unchainedAPI.modules.worker.ensureNoWork({
32
+ type: workConfig.type,
33
+ priority: workConfig.priority || 0,
34
+ scheduleId,
35
+ });
36
+ return null;
37
+ }
38
+ const fixedSchedule = { ...workConfig.schedule };
39
+ fixedSchedule.schedules[0].s = [0]; // ignore seconds, always run on second 0
40
+ const nextDate = later.schedule(fixedSchedule).next(1, referenceDate);
41
+ nextDate.setMilliseconds(0);
42
+ const workData = {
43
+ type: workConfig.type,
44
+ scheduleId,
45
+ scheduled: nextDate,
46
+ timeout: workConfig.timeout,
47
+ priority: workConfig.priority || 0,
48
+ retries: workConfig.retries || 0,
49
+ };
50
+ if (workConfig.input) {
51
+ workData.input = await workConfig.input(workData);
52
+ // A work input fn can skip auto scheduling a new record
53
+ // when it explicitly returns a falsish input instead of a dictionary
54
+ if (!workData.input)
55
+ return null;
56
+ }
57
+ return unchainedAPI.modules.worker.ensureOneWork(workData);
58
+ }));
59
+ },
60
+ process: async ({ maxWorkItemCount = 10, referenceDate }) => {
61
+ await workerActions.autorescheduleTypes({
62
+ referenceDate,
63
+ });
64
+ let processedCount = 0;
65
+ while (true) {
66
+ if (maxWorkItemCount && maxWorkItemCount <= processedCount)
67
+ break;
68
+ const doneWork = await WorkerDirector.processNextWork(unchainedAPI, workerId);
69
+ if (!doneWork)
70
+ break;
71
+ processedCount++;
72
+ }
73
+ },
74
+ };
75
+ workerActions.reset();
76
+ return workerActions;
77
+ },
78
+ };
79
+ //# sourceMappingURL=BaseWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseWorker.js","sourceRoot":"","sources":["../../src/directors/BaseWorker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { IWorker } from './BaseWorker.js';
2
+ export interface EventListenerWorkerParams {
3
+ workerId?: string;
4
+ }
5
+ export declare const EventListenerWorker: IWorker<EventListenerWorkerParams>;
6
+ //# sourceMappingURL=EventListenerWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventListenerWorker.d.ts","sourceRoot":"","sources":["../../src/directors/EventListenerWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAkDtD,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,mBAAmB,EAAE,OAAO,CAAC,yBAAyB,CA6ClE,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { subscribe } from '@unchainedshop/events';
2
+ import { BaseWorker } from './BaseWorker.js';
3
+ import { WorkerEventTypes } from '@unchainedshop/core-worker';
4
+ import { setTimeout } from 'node:timers/promises';
5
+ import { createLogger } from '@unchainedshop/logger';
6
+ const logger = createLogger('unchained:worker');
7
+ function debounce(func, wait) {
8
+ let abortController = null;
9
+ const debounced = (async (...args) => {
10
+ // Cancel any pending execution
11
+ if (abortController) {
12
+ abortController.abort();
13
+ }
14
+ // Create new abort controller for this execution
15
+ abortController = new AbortController();
16
+ try {
17
+ // Wait for the debounce period
18
+ await setTimeout(wait, undefined, { signal: abortController.signal });
19
+ // If we reach here, the timeout completed without being aborted
20
+ const result = await func(...args);
21
+ abortController = null;
22
+ return result;
23
+ }
24
+ catch (error) {
25
+ // If the operation was aborted, don't execute the function
26
+ if (error.name === 'AbortError') {
27
+ return;
28
+ }
29
+ // Re-throw any other errors
30
+ throw error;
31
+ }
32
+ });
33
+ debounced.cancel = () => {
34
+ if (abortController) {
35
+ abortController.abort();
36
+ abortController = null;
37
+ }
38
+ };
39
+ return debounced;
40
+ }
41
+ export const EventListenerWorker = {
42
+ ...BaseWorker,
43
+ key: 'shop.unchained.worker.event-listener',
44
+ label: 'Allocates work on events. This worker does not make sense on multiple containers.',
45
+ version: '1.0.0',
46
+ type: 'EVENT_LISTENER',
47
+ actions: ({ workerId }, unchainedAPI) => {
48
+ const baseWorkerActions = BaseWorker.actions({ workerId, worker: EventListenerWorker }, unchainedAPI);
49
+ // Debounce in the event of many work queue events conflicting with each other
50
+ const processWorkQueue = debounce(async () => {
51
+ try {
52
+ await baseWorkerActions.process({
53
+ maxWorkItemCount: 1, // only one work item at a time, else we could end up in a loop
54
+ referenceDate: EventListenerWorker.getFloorDate(),
55
+ });
56
+ }
57
+ catch (error) {
58
+ // Log error but don't crash the worker
59
+ logger.error(error);
60
+ }
61
+ }, 300);
62
+ return {
63
+ ...baseWorkerActions,
64
+ async start() {
65
+ subscribe(WorkerEventTypes.ADDED, processWorkQueue);
66
+ subscribe(WorkerEventTypes.FINISHED, processWorkQueue);
67
+ await setTimeout(300);
68
+ await baseWorkerActions.autorescheduleTypes({
69
+ referenceDate: EventListenerWorker.getFloorDate(),
70
+ });
71
+ },
72
+ stop() {
73
+ processWorkQueue.cancel();
74
+ },
75
+ };
76
+ },
77
+ };
78
+ //# sourceMappingURL=EventListenerWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventListenerWorker.js","sourceRoot":"","sources":["../../src/directors/EventListenerWorker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { WorkData } from '@unchainedshop/core-worker';
2
+ export interface FailedReschedulerParams {
3
+ transformRetry?: (workData: WorkData) => Promise<WorkData | null>;
4
+ }
5
+ export interface IScheduler<P> {
6
+ key: string;
7
+ label: string;
8
+ version: string;
9
+ actions: (params: P, unchainedAPI: any) => {
10
+ start: () => void;
11
+ stop: () => void;
12
+ };
13
+ }
14
+ export declare const FailedRescheduler: IScheduler<FailedReschedulerParams>;
15
+ //# sourceMappingURL=FailedRescheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailedRescheduler.d.ts","sourceRoot":"","sources":["../../src/directors/FailedRescheduler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,QAAQ,EAAoB,MAAM,4BAA4B,CAAC;AAE9E,MAAM,WAAW,uBAAuB;IACtC,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAEhB,OAAO,EAAE,CACP,MAAM,EAAE,CAAC,EACT,YAAY,KAAA,KACT;QACH,KAAK,EAAE,MAAM,IAAI,CAAC;QAClB,IAAI,EAAE,MAAM,IAAI,CAAC;KAClB,CAAC;CACH;AAED,eAAO,MAAM,iBAAiB,EAAE,UAAU,CAAC,uBAAuB,CAkDjE,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { defaultLogger } from '@unchainedshop/logger';
2
+ import { subscribe } from '@unchainedshop/events';
3
+ import { WorkerEventTypes } from '@unchainedshop/core-worker';
4
+ export const FailedRescheduler = {
5
+ key: 'shop.unchained.scheduler.failed',
6
+ label: 'Reschedule failed works',
7
+ version: '1.0.0',
8
+ actions: ({ transformRetry }, unchainedAPI) => {
9
+ const handleFinishedWork = async ({ payload: work }) => {
10
+ if (!work.success && work.retries > 0) {
11
+ const now = new Date();
12
+ const workDelayMs = work.scheduled.getTime() - work.created.getTime();
13
+ // In short: Double the delay of the old work or delay for 5 seconds
14
+ const scheduled = workDelayMs > 1000
15
+ ? new Date(now.getTime() + workDelayMs * 2)
16
+ : new Date(now.setSeconds(now.getSeconds() + 5));
17
+ defaultLogger.error(`${FailedRescheduler.key} -> Reschedule failed work ${work._id} ${work.type} for ${scheduled.toISOString()} (in ${Math.round(workDelayMs / 1000)}). Remaining retries: ${work.retries}`);
18
+ const newWorkData = {
19
+ type: work.type,
20
+ priority: work.priority,
21
+ originalWorkId: work.originalWorkId || work._id,
22
+ retries: work.retries - 1,
23
+ timeout: work.timeout,
24
+ input: work.input,
25
+ scheduled,
26
+ };
27
+ await unchainedAPI.modules.worker.addWork(transformRetry ? await transformRetry(newWorkData) : newWorkData);
28
+ }
29
+ };
30
+ return {
31
+ start() {
32
+ subscribe(WorkerEventTypes.FINISHED, handleFinishedWork);
33
+ },
34
+ stop() {
35
+ /* */
36
+ },
37
+ };
38
+ },
39
+ };
40
+ //# sourceMappingURL=FailedRescheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FailedRescheduler.js","sourceRoot":"","sources":["../../src/directors/FailedRescheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAkB,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAoB9E,MAAM,CAAC,MAAM,iBAAiB,GAAwC;IACpE,GAAG,EAAE,iCAAiC;IACtC,KAAK,EAAE,yBAAyB;IAChC,OAAO,EAAE,OAAO;IAEhB,OAAO,EAAE,CAAC,EAAE,cAAc,EAAE,EAAE,YAAY,EAAE,EAAE;QAC5C,MAAM,kBAAkB,GAAG,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAqB,EAAE,EAAE;YACxE,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;gBACtC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBAEtE,oEAAoE;gBACpE,MAAM,SAAS,GACb,WAAW,GAAG,IAAI;oBAChB,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,WAAW,GAAG,CAAC,CAAC;oBAC3C,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;gBAErD,aAAa,CAAC,KAAK,CACjB,GAAG,iBAAiB,CAAC,GAAG,8BAA8B,IAAI,CAAC,GAAG,IAC5D,IAAI,CAAC,IACP,QAAQ,SAAS,CAAC,WAAW,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,yBACnE,IAAI,CAAC,OACP,EAAE,CACH,CAAC;gBAEF,MAAM,WAAW,GAAG;oBAClB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,GAAG;oBAC/C,OAAO,EAAE,IAAI,CAAC,OAAO,GAAG,CAAC;oBACzB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,SAAS;iBACV,CAAC;gBACF,MAAM,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CACvC,cAAc,CAAC,CAAC,CAAC,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CACjE,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QAEF,OAAO;YACL,KAAK;gBACH,SAAS,CAAO,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;YACjE,CAAC;YAED,IAAI;gBACF,KAAK;YACP,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { ScheduleData } from '@breejs/later';
2
+ import { IWorker } from './BaseWorker.js';
3
+ export interface IntervalWorkerParams {
4
+ workerId?: string;
5
+ batchCount?: number;
6
+ schedule?: ScheduleData;
7
+ }
8
+ export declare const scheduleToInterval: (schedule: ScheduleData) => number;
9
+ export declare const IntervalWorker: IWorker<IntervalWorkerParams>;
10
+ //# sourceMappingURL=IntervalWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntervalWorker.d.ts","sourceRoot":"","sources":["../../src/directors/IntervalWorker.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAc,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAOtD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAMD,eAAO,MAAM,kBAAkB,GAAI,UAAU,YAAY,WAWxD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,oBAAoB,CA2CxD,CAAC"}
@@ -0,0 +1,55 @@
1
+ import later from '@breejs/later';
2
+ import { BaseWorker } from './BaseWorker.js';
3
+ import { createLogger } from '@unchainedshop/logger';
4
+ const { NODE_ENV } = process.env;
5
+ const logger = createLogger('unchained:worker');
6
+ const defaultSchedule = later.parse.text(NODE_ENV !== 'production' ? 'every 2 seconds' : 'every 30 seconds');
7
+ export const scheduleToInterval = (schedule) => {
8
+ const referenceDate = new Date(1000);
9
+ const nextDates = later.schedule(schedule).next(2, referenceDate);
10
+ if (!nextDates || nextDates.length < 2) {
11
+ throw new Error('Schedule must produce at least 2 consecutive dates');
12
+ }
13
+ const [one, two] = nextDates;
14
+ const diff = new Date(two).getTime() - new Date(one).getTime();
15
+ return Math.min(1000 * 60 * 60, diff); // at least once every hour!
16
+ };
17
+ export const IntervalWorker = {
18
+ ...BaseWorker,
19
+ key: 'shop.unchained.worker.cron',
20
+ label: 'Allocates work on fixed intervals with native node setInterval',
21
+ version: '1.0.0',
22
+ type: 'CRON',
23
+ actions: ({ workerId, batchCount = 10, schedule = defaultSchedule }, unchainedAPI) => {
24
+ const baseWorkerActions = BaseWorker.actions({ workerId, worker: IntervalWorker }, unchainedAPI);
25
+ const intervalDelay = scheduleToInterval(schedule);
26
+ let intervalHandle = null;
27
+ return {
28
+ ...baseWorkerActions,
29
+ start() {
30
+ // Prevent multiple intervals from running
31
+ if (intervalHandle) {
32
+ return;
33
+ }
34
+ intervalHandle = setInterval(async () => {
35
+ try {
36
+ await baseWorkerActions.process({
37
+ maxWorkItemCount: batchCount,
38
+ referenceDate: IntervalWorker.getFloorDate(),
39
+ });
40
+ }
41
+ catch (error) {
42
+ logger.error(error);
43
+ }
44
+ }, intervalDelay);
45
+ },
46
+ stop() {
47
+ if (intervalHandle) {
48
+ clearInterval(intervalHandle);
49
+ intervalHandle = null;
50
+ }
51
+ },
52
+ };
53
+ },
54
+ };
55
+ //# sourceMappingURL=IntervalWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntervalWorker.js","sourceRoot":"","sources":["../../src/directors/IntervalWorker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAuB,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,UAAU,EAAW,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAEjC,MAAM,MAAM,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAC;AAQhD,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CACtC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,CACnE,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,QAAsB,EAAE,EAAE;IAC3D,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,aAAa,CAAW,CAAC;IAE5E,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,4BAA4B;AACrE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAkC;IAC3D,GAAG,UAAU;IAEb,GAAG,EAAE,4BAA4B;IACjC,KAAK,EAAE,gEAAgE;IACvE,OAAO,EAAE,OAAO;IAChB,IAAI,EAAE,MAAM;IAEZ,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,GAAG,EAAE,EAAE,QAAQ,GAAG,eAAe,EAAE,EAAE,YAAY,EAAE,EAAE;QACnF,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,YAAY,CAAC,CAAC;QAEjG,MAAM,aAAa,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,cAAc,GAA0B,IAAI,CAAC;QAEjD,OAAO;YACL,GAAG,iBAAiB;YAEpB,KAAK;gBACH,0CAA0C;gBAC1C,IAAI,cAAc,EAAE,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,cAAc,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;oBACtC,IAAI,CAAC;wBACH,MAAM,iBAAiB,CAAC,OAAO,CAAC;4BAC9B,gBAAgB,EAAE,UAAU;4BAC5B,aAAa,EAAE,cAAc,CAAC,YAAY,EAAE;yBAC7C,CAAC,CAAC;oBACL,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;oBACtB,CAAC;gBACH,CAAC,EAAE,aAAa,CAAC,CAAC;YACpB,CAAC;YAED,IAAI;gBACF,IAAI,cAAc,EAAE,CAAC;oBACnB,aAAa,CAAC,cAAc,CAAC,CAAC;oBAC9B,cAAc,GAAG,IAAI,CAAC;gBACxB,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import { File } from '@unchainedshop/core-files';
2
2
  import { Modules } from '../modules.js';
3
- export declare function uploadFileFromStreamService(this: Modules, { directoryName, rawFile, meta }: {
3
+ export declare function uploadFileFromStreamService(this: Modules, { directoryName, rawFile, meta, ...options }: {
4
4
  directoryName: string;
5
5
  rawFile: any;
6
6
  meta?: any;
7
- }): Promise<File>;
7
+ } & Record<string, any>): Promise<File>;
8
8
  //# sourceMappingURL=uploadFileFromStream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"uploadFileFromStream.d.ts","sourceRoot":"","sources":["../../src/services/uploadFileFromStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,OAAO,EACb,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,GACpF,OAAO,CAAC,IAAI,CAAC,CAQf"}
1
+ {"version":3,"file":"uploadFileFromStream.d.ts","sourceRoot":"","sources":["../../src/services/uploadFileFromStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,OAAO,EACb,EACE,aAAa,EACb,OAAO,EACP,IAAI,EACJ,GAAG,OAAO,EACX,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC3E,OAAO,CAAC,IAAI,CAAC,CAaf"}
@@ -1,9 +1,9 @@
1
1
  import { getFileFromFileData, getFileAdapter } from '@unchainedshop/core-files';
2
- export async function uploadFileFromStreamService({ directoryName, rawFile, meta }) {
2
+ export async function uploadFileFromStreamService({ directoryName, rawFile, meta, ...options }) {
3
3
  const fileUploadAdapter = getFileAdapter();
4
4
  const uploadFileData = await fileUploadAdapter.uploadFileFromStream(directoryName, rawFile, {
5
5
  modules: this,
6
- });
6
+ }, options);
7
7
  const fileData = getFileFromFileData(uploadFileData, meta);
8
8
  const fileId = await this.files.create(fileData);
9
9
  return this.files.findFile({ fileId });
@@ -1 +1 @@
1
- {"version":3,"file":"uploadFileFromStream.js","sourceRoot":"","sources":["../../src/services/uploadFileFromStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAQ,MAAM,2BAA2B,CAAC;AAGtF,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAE/C,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAuD;IAErF,MAAM,iBAAiB,GAAG,cAAc,EAAE,CAAC;IAC3C,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,oBAAoB,CAAC,aAAa,EAAE,OAAO,EAAE;QAC1F,OAAO,EAAE,IAAI;KACd,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,mBAAmB,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"uploadFileFromStream.js","sourceRoot":"","sources":["../../src/services/uploadFileFromStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAQ,MAAM,2BAA2B,CAAC;AAGtF,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAE/C,EACE,aAAa,EACb,OAAO,EACP,IAAI,EACJ,GAAG,OAAO,EACgE;IAE5E,MAAM,iBAAiB,GAAG,cAAc,EAAE,CAAC;IAC3C,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,oBAAoB,CACjE,aAAa,EACb,OAAO,EACP;QACE,OAAO,EAAE,IAAI;KACd,EACD,OAAO,CACR,CAAC;IACF,MAAM,QAAQ,GAAG,mBAAmB,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AACzC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unchainedshop/core",
3
- "version": "3.1.11",
3
+ "version": "3.1.15",
4
4
  "main": "lib/core-index.js",
5
5
  "types": "lib/core-index.d.ts",
6
6
  "type": "module",
@@ -32,29 +32,29 @@
32
32
  "homepage": "https://github.com/unchainedshop/unchained#readme",
33
33
  "dependencies": {
34
34
  "@breejs/later": "^4.2.0",
35
- "@unchainedshop/core-assortments": "^3.1.7",
36
- "@unchainedshop/core-bookmarks": "^3.1.7",
37
- "@unchainedshop/core-countries": "^3.1.7",
38
- "@unchainedshop/core-currencies": "^3.1.7",
39
- "@unchainedshop/core-delivery": "^3.1.7",
40
- "@unchainedshop/core-enrollments": "^3.1.7",
41
- "@unchainedshop/core-events": "^3.1.7",
42
- "@unchainedshop/core-files": "^3.1.7",
43
- "@unchainedshop/core-filters": "^3.1.7",
44
- "@unchainedshop/core-languages": "^3.1.7",
45
- "@unchainedshop/core-orders": "^3.1.7",
46
- "@unchainedshop/core-payment": "^3.1.7",
47
- "@unchainedshop/core-products": "^3.1.7",
48
- "@unchainedshop/core-quotations": "^3.1.7",
49
- "@unchainedshop/core-users": "^3.1.7",
50
- "@unchainedshop/core-warehousing": "^3.1.7",
51
- "@unchainedshop/core-worker": "^3.1.7",
52
- "@unchainedshop/logger": "^3.1.7",
53
- "@unchainedshop/utils": "^3.1.7"
35
+ "@unchainedshop/core-assortments": "^3.1.13",
36
+ "@unchainedshop/core-bookmarks": "^3.1.13",
37
+ "@unchainedshop/core-countries": "^3.1.13",
38
+ "@unchainedshop/core-currencies": "^3.1.13",
39
+ "@unchainedshop/core-delivery": "^3.1.13",
40
+ "@unchainedshop/core-enrollments": "^3.1.13",
41
+ "@unchainedshop/core-events": "^3.1.13",
42
+ "@unchainedshop/core-files": "^3.1.13",
43
+ "@unchainedshop/core-filters": "^3.1.13",
44
+ "@unchainedshop/core-languages": "^3.1.13",
45
+ "@unchainedshop/core-orders": "^3.1.13",
46
+ "@unchainedshop/core-payment": "^3.1.13",
47
+ "@unchainedshop/core-products": "^3.1.13",
48
+ "@unchainedshop/core-quotations": "^3.1.13",
49
+ "@unchainedshop/core-users": "^3.1.13",
50
+ "@unchainedshop/core-warehousing": "^3.1.13",
51
+ "@unchainedshop/core-worker": "^3.1.13",
52
+ "@unchainedshop/logger": "^3.1.13",
53
+ "@unchainedshop/utils": "^3.1.13"
54
54
  },
55
55
  "devDependencies": {
56
- "@types/node": "^22.13.14",
57
- "tsx": "^4.19.3",
58
- "typescript": "^5.8.2"
56
+ "@types/node": "^24.0.4",
57
+ "tsx": "^4.20.3",
58
+ "typescript": "^5.8.3"
59
59
  }
60
60
  }
@@ -3,12 +3,22 @@ import { Modules } from '../modules.js';
3
3
 
4
4
  export async function uploadFileFromStreamService(
5
5
  this: Modules,
6
- { directoryName, rawFile, meta }: { directoryName: string; rawFile: any; meta?: any },
6
+ {
7
+ directoryName,
8
+ rawFile,
9
+ meta,
10
+ ...options
11
+ }: { directoryName: string; rawFile: any; meta?: any } & Record<string, any>,
7
12
  ): Promise<File> {
8
13
  const fileUploadAdapter = getFileAdapter();
9
- const uploadFileData = await fileUploadAdapter.uploadFileFromStream(directoryName, rawFile, {
10
- modules: this,
11
- });
14
+ const uploadFileData = await fileUploadAdapter.uploadFileFromStream(
15
+ directoryName,
16
+ rawFile,
17
+ {
18
+ modules: this,
19
+ },
20
+ options,
21
+ );
12
22
  const fileData = getFileFromFileData(uploadFileData, meta);
13
23
  const fileId = await this.files.create(fileData);
14
24
  return this.files.findFile({ fileId });