@aghamdi/shared-types 0.2.39 → 0.2.40
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/dist/index.d.mts +34 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +128 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +127 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -70,6 +70,30 @@ declare class MiaNotifications {
|
|
|
70
70
|
declare class MessagingBus {
|
|
71
71
|
static subscribe(channel: string, queue: string, onMessage: Function): Promise<void>;
|
|
72
72
|
static publish(channel: string, message: any): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Idempotently ensure a JetStream stream exists for `streamSubjects`.
|
|
75
|
+
* Tolerates create races between concurrent callers.
|
|
76
|
+
*/
|
|
77
|
+
private static ensureStream;
|
|
78
|
+
/**
|
|
79
|
+
* Durable publish: persists the message to a JetStream stream and resolves
|
|
80
|
+
* ONLY after the server acknowledges the store. Unlike `publish` (fire-and-
|
|
81
|
+
* forget core NATS), a resolved `publishDurable` means the message survives
|
|
82
|
+
* even when no subscriber is currently connected — use it for alerts/events
|
|
83
|
+
* that must never be lost while a consumer is down.
|
|
84
|
+
*/
|
|
85
|
+
static publishDurable(channel: string, message: any, streamName: string, streamSubjects?: string[]): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Durable subscribe: binds a durable JetStream consumer so messages
|
|
88
|
+
* published while this service was down are REPLAYED when it reconnects
|
|
89
|
+
* (unlike `subscribe`, which only delivers live messages). Each message is
|
|
90
|
+
* acked after the handler resolves; a handler that throws naks for redelivery
|
|
91
|
+
* (bounded by maxDeliver so a poison message can't loop forever).
|
|
92
|
+
*/
|
|
93
|
+
static subscribeDurable(channel: string, streamName: string, durableName: string, onMessage: (message: any) => Promise<void> | void, options?: {
|
|
94
|
+
streamSubjects?: string[];
|
|
95
|
+
maxDeliver?: number;
|
|
96
|
+
}): Promise<void>;
|
|
73
97
|
}
|
|
74
98
|
|
|
75
99
|
interface CustomerMobileVerifiedMessage {
|
|
@@ -375,7 +399,16 @@ interface CriticalErrorEvent {
|
|
|
375
399
|
resourceType?: string;
|
|
376
400
|
occurredAt: string;
|
|
377
401
|
}
|
|
402
|
+
declare const CRITICAL_ERRORS_STREAM = "CRITICAL_ERRORS";
|
|
403
|
+
declare const CRITICAL_ERRORS_SUBJECT = "mia.errors.critical";
|
|
378
404
|
declare class MiaCriticalErrors {
|
|
405
|
+
/**
|
|
406
|
+
* Publish a critical-error alert durably. Resolves only once the alert is
|
|
407
|
+
* persisted to the CRITICAL_ERRORS JetStream stream, so a resolved call
|
|
408
|
+
* guarantees the alert survives even if the notifications service is down —
|
|
409
|
+
* it is replayed to that durable consumer on recovery. Callers may therefore
|
|
410
|
+
* treat a resolved publish as "the operator will be notified".
|
|
411
|
+
*/
|
|
379
412
|
static publish(event: CriticalErrorEvent): Promise<void>;
|
|
380
413
|
}
|
|
381
414
|
|
|
@@ -444,4 +477,4 @@ declare class MiaExpenses {
|
|
|
444
477
|
static publishPurchaseOrderCancelled(event: PurchaseOrderCancelledEvent): Promise<void>;
|
|
445
478
|
}
|
|
446
479
|
|
|
447
|
-
export { type AttributeValue, type AuditRiskLevel, type AuditTraceEvent, type BuyerIdentificationScheme, type CriticalErrorEvent, type CustomerActionMessage, type CustomerAddTagMessage, type CustomerInfo, type CustomerKind, type CustomerMobileVerifiedMessage, type CustomerRole, type CustomerUpdateNotesMessage, type DeviceNotification, type LowStockEvent, MessagingBus, MiaAuditTrace, MiaCriticalErrors, MiaExpenses, MiaNotifications, MiaOrders, MiaPreorders, MiaServerResponse, MiaShifts, MiaStock, MicrogemsCustomer, type MultilanguageString, type Notification, type OperateOrderNotification, type OperateOrderNotificationPayload, type OrderAttribute, type OrderCreatedEvent, type OrderFeeInfo, type OrderItemAttributes, type OrderItemForInvoice, type PaymentInfo, type PreorderPaymentEvent, type PrintLine, type PurchaseOrderCancelledEvent, type PurchaseOrderRecordedEvent, type PurchaseOrderStatus, type RawMaterialKind, type SelectedOrderAttribute, type ServiceType, type ShiftClosedEvent, type SmsNotification, type StaffNotification, type StockTransactionType, type TopicNotification, type UserNotification, calculateAttributesPrice, createMultilanguageString, getLocalizedText, validateSelectedAttributes };
|
|
480
|
+
export { type AttributeValue, type AuditRiskLevel, type AuditTraceEvent, type BuyerIdentificationScheme, CRITICAL_ERRORS_STREAM, CRITICAL_ERRORS_SUBJECT, type CriticalErrorEvent, type CustomerActionMessage, type CustomerAddTagMessage, type CustomerInfo, type CustomerKind, type CustomerMobileVerifiedMessage, type CustomerRole, type CustomerUpdateNotesMessage, type DeviceNotification, type LowStockEvent, MessagingBus, MiaAuditTrace, MiaCriticalErrors, MiaExpenses, MiaNotifications, MiaOrders, MiaPreorders, MiaServerResponse, MiaShifts, MiaStock, MicrogemsCustomer, type MultilanguageString, type Notification, type OperateOrderNotification, type OperateOrderNotificationPayload, type OrderAttribute, type OrderCreatedEvent, type OrderFeeInfo, type OrderItemAttributes, type OrderItemForInvoice, type PaymentInfo, type PreorderPaymentEvent, type PrintLine, type PurchaseOrderCancelledEvent, type PurchaseOrderRecordedEvent, type PurchaseOrderStatus, type RawMaterialKind, type SelectedOrderAttribute, type ServiceType, type ShiftClosedEvent, type SmsNotification, type StaffNotification, type StockTransactionType, type TopicNotification, type UserNotification, calculateAttributesPrice, createMultilanguageString, getLocalizedText, validateSelectedAttributes };
|
package/dist/index.d.ts
CHANGED
|
@@ -70,6 +70,30 @@ declare class MiaNotifications {
|
|
|
70
70
|
declare class MessagingBus {
|
|
71
71
|
static subscribe(channel: string, queue: string, onMessage: Function): Promise<void>;
|
|
72
72
|
static publish(channel: string, message: any): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Idempotently ensure a JetStream stream exists for `streamSubjects`.
|
|
75
|
+
* Tolerates create races between concurrent callers.
|
|
76
|
+
*/
|
|
77
|
+
private static ensureStream;
|
|
78
|
+
/**
|
|
79
|
+
* Durable publish: persists the message to a JetStream stream and resolves
|
|
80
|
+
* ONLY after the server acknowledges the store. Unlike `publish` (fire-and-
|
|
81
|
+
* forget core NATS), a resolved `publishDurable` means the message survives
|
|
82
|
+
* even when no subscriber is currently connected — use it for alerts/events
|
|
83
|
+
* that must never be lost while a consumer is down.
|
|
84
|
+
*/
|
|
85
|
+
static publishDurable(channel: string, message: any, streamName: string, streamSubjects?: string[]): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Durable subscribe: binds a durable JetStream consumer so messages
|
|
88
|
+
* published while this service was down are REPLAYED when it reconnects
|
|
89
|
+
* (unlike `subscribe`, which only delivers live messages). Each message is
|
|
90
|
+
* acked after the handler resolves; a handler that throws naks for redelivery
|
|
91
|
+
* (bounded by maxDeliver so a poison message can't loop forever).
|
|
92
|
+
*/
|
|
93
|
+
static subscribeDurable(channel: string, streamName: string, durableName: string, onMessage: (message: any) => Promise<void> | void, options?: {
|
|
94
|
+
streamSubjects?: string[];
|
|
95
|
+
maxDeliver?: number;
|
|
96
|
+
}): Promise<void>;
|
|
73
97
|
}
|
|
74
98
|
|
|
75
99
|
interface CustomerMobileVerifiedMessage {
|
|
@@ -375,7 +399,16 @@ interface CriticalErrorEvent {
|
|
|
375
399
|
resourceType?: string;
|
|
376
400
|
occurredAt: string;
|
|
377
401
|
}
|
|
402
|
+
declare const CRITICAL_ERRORS_STREAM = "CRITICAL_ERRORS";
|
|
403
|
+
declare const CRITICAL_ERRORS_SUBJECT = "mia.errors.critical";
|
|
378
404
|
declare class MiaCriticalErrors {
|
|
405
|
+
/**
|
|
406
|
+
* Publish a critical-error alert durably. Resolves only once the alert is
|
|
407
|
+
* persisted to the CRITICAL_ERRORS JetStream stream, so a resolved call
|
|
408
|
+
* guarantees the alert survives even if the notifications service is down —
|
|
409
|
+
* it is replayed to that durable consumer on recovery. Callers may therefore
|
|
410
|
+
* treat a resolved publish as "the operator will be notified".
|
|
411
|
+
*/
|
|
379
412
|
static publish(event: CriticalErrorEvent): Promise<void>;
|
|
380
413
|
}
|
|
381
414
|
|
|
@@ -444,4 +477,4 @@ declare class MiaExpenses {
|
|
|
444
477
|
static publishPurchaseOrderCancelled(event: PurchaseOrderCancelledEvent): Promise<void>;
|
|
445
478
|
}
|
|
446
479
|
|
|
447
|
-
export { type AttributeValue, type AuditRiskLevel, type AuditTraceEvent, type BuyerIdentificationScheme, type CriticalErrorEvent, type CustomerActionMessage, type CustomerAddTagMessage, type CustomerInfo, type CustomerKind, type CustomerMobileVerifiedMessage, type CustomerRole, type CustomerUpdateNotesMessage, type DeviceNotification, type LowStockEvent, MessagingBus, MiaAuditTrace, MiaCriticalErrors, MiaExpenses, MiaNotifications, MiaOrders, MiaPreorders, MiaServerResponse, MiaShifts, MiaStock, MicrogemsCustomer, type MultilanguageString, type Notification, type OperateOrderNotification, type OperateOrderNotificationPayload, type OrderAttribute, type OrderCreatedEvent, type OrderFeeInfo, type OrderItemAttributes, type OrderItemForInvoice, type PaymentInfo, type PreorderPaymentEvent, type PrintLine, type PurchaseOrderCancelledEvent, type PurchaseOrderRecordedEvent, type PurchaseOrderStatus, type RawMaterialKind, type SelectedOrderAttribute, type ServiceType, type ShiftClosedEvent, type SmsNotification, type StaffNotification, type StockTransactionType, type TopicNotification, type UserNotification, calculateAttributesPrice, createMultilanguageString, getLocalizedText, validateSelectedAttributes };
|
|
480
|
+
export { type AttributeValue, type AuditRiskLevel, type AuditTraceEvent, type BuyerIdentificationScheme, CRITICAL_ERRORS_STREAM, CRITICAL_ERRORS_SUBJECT, type CriticalErrorEvent, type CustomerActionMessage, type CustomerAddTagMessage, type CustomerInfo, type CustomerKind, type CustomerMobileVerifiedMessage, type CustomerRole, type CustomerUpdateNotesMessage, type DeviceNotification, type LowStockEvent, MessagingBus, MiaAuditTrace, MiaCriticalErrors, MiaExpenses, MiaNotifications, MiaOrders, MiaPreorders, MiaServerResponse, MiaShifts, MiaStock, MicrogemsCustomer, type MultilanguageString, type Notification, type OperateOrderNotification, type OperateOrderNotificationPayload, type OrderAttribute, type OrderCreatedEvent, type OrderFeeInfo, type OrderItemAttributes, type OrderItemForInvoice, type PaymentInfo, type PreorderPaymentEvent, type PrintLine, type PurchaseOrderCancelledEvent, type PurchaseOrderRecordedEvent, type PurchaseOrderStatus, type RawMaterialKind, type SelectedOrderAttribute, type ServiceType, type ShiftClosedEvent, type SmsNotification, type StaffNotification, type StockTransactionType, type TopicNotification, type UserNotification, calculateAttributesPrice, createMultilanguageString, getLocalizedText, validateSelectedAttributes };
|
package/dist/index.js
CHANGED
|
@@ -61,6 +61,8 @@ var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")])
|
|
|
61
61
|
// src/index.ts
|
|
62
62
|
var src_exports = {};
|
|
63
63
|
__export(src_exports, {
|
|
64
|
+
CRITICAL_ERRORS_STREAM: () => CRITICAL_ERRORS_STREAM,
|
|
65
|
+
CRITICAL_ERRORS_SUBJECT: () => CRITICAL_ERRORS_SUBJECT,
|
|
64
66
|
MessagingBus: () => MessagingBus,
|
|
65
67
|
MiaAuditTrace: () => MiaAuditTrace,
|
|
66
68
|
MiaCriticalErrors: () => MiaCriticalErrors,
|
|
@@ -109,7 +111,8 @@ MiaServerResponse.sendError = (error, status = 400) => {
|
|
|
109
111
|
|
|
110
112
|
// src/messaging-bus.ts
|
|
111
113
|
var import_nats = require("nats");
|
|
112
|
-
var
|
|
114
|
+
var SEVEN_DAYS_NS = 7 * 24 * 60 * 60 * 1e9;
|
|
115
|
+
var MessagingBus = class _MessagingBus {
|
|
113
116
|
static subscribe(channel, queue, onMessage) {
|
|
114
117
|
return __async(this, null, function* () {
|
|
115
118
|
if (!process.env.NATS_HOST) {
|
|
@@ -151,6 +154,113 @@ var MessagingBus = class {
|
|
|
151
154
|
yield nc.drain();
|
|
152
155
|
});
|
|
153
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Idempotently ensure a JetStream stream exists for `streamSubjects`.
|
|
159
|
+
* Tolerates create races between concurrent callers.
|
|
160
|
+
*/
|
|
161
|
+
static ensureStream(jsm, streamName, streamSubjects) {
|
|
162
|
+
return __async(this, null, function* () {
|
|
163
|
+
try {
|
|
164
|
+
yield jsm.streams.info(streamName);
|
|
165
|
+
} catch (e) {
|
|
166
|
+
try {
|
|
167
|
+
yield jsm.streams.add({
|
|
168
|
+
name: streamName,
|
|
169
|
+
subjects: streamSubjects,
|
|
170
|
+
storage: import_nats.StorageType.File,
|
|
171
|
+
max_age: SEVEN_DAYS_NS
|
|
172
|
+
});
|
|
173
|
+
} catch (e2) {
|
|
174
|
+
yield jsm.streams.info(streamName);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Durable publish: persists the message to a JetStream stream and resolves
|
|
181
|
+
* ONLY after the server acknowledges the store. Unlike `publish` (fire-and-
|
|
182
|
+
* forget core NATS), a resolved `publishDurable` means the message survives
|
|
183
|
+
* even when no subscriber is currently connected — use it for alerts/events
|
|
184
|
+
* that must never be lost while a consumer is down.
|
|
185
|
+
*/
|
|
186
|
+
static publishDurable(_0, _1, _2) {
|
|
187
|
+
return __async(this, arguments, function* (channel, message, streamName, streamSubjects = [channel]) {
|
|
188
|
+
if (!process.env.NATS_HOST) {
|
|
189
|
+
throw new Error("NATS_HOST ENV is not defined");
|
|
190
|
+
}
|
|
191
|
+
const nc = yield (0, import_nats.connect)({ servers: process.env.NATS_HOST });
|
|
192
|
+
try {
|
|
193
|
+
const jsm = yield nc.jetstreamManager();
|
|
194
|
+
yield _MessagingBus.ensureStream(jsm, streamName, streamSubjects);
|
|
195
|
+
const jc = (0, import_nats.JSONCodec)();
|
|
196
|
+
yield nc.jetstream().publish(channel, jc.encode(message));
|
|
197
|
+
} finally {
|
|
198
|
+
yield nc.drain();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Durable subscribe: binds a durable JetStream consumer so messages
|
|
204
|
+
* published while this service was down are REPLAYED when it reconnects
|
|
205
|
+
* (unlike `subscribe`, which only delivers live messages). Each message is
|
|
206
|
+
* acked after the handler resolves; a handler that throws naks for redelivery
|
|
207
|
+
* (bounded by maxDeliver so a poison message can't loop forever).
|
|
208
|
+
*/
|
|
209
|
+
static subscribeDurable(_0, _1, _2, _3) {
|
|
210
|
+
return __async(this, arguments, function* (channel, streamName, durableName, onMessage, options = {}) {
|
|
211
|
+
var _a, _b;
|
|
212
|
+
if (!process.env.NATS_HOST) {
|
|
213
|
+
throw new Error("NATS_HOST ENV is not defined");
|
|
214
|
+
}
|
|
215
|
+
const streamSubjects = (_a = options.streamSubjects) != null ? _a : [channel];
|
|
216
|
+
const maxDeliver = (_b = options.maxDeliver) != null ? _b : 10;
|
|
217
|
+
const nc = yield (0, import_nats.connect)({ servers: process.env.NATS_HOST });
|
|
218
|
+
const jsm = yield nc.jetstreamManager();
|
|
219
|
+
yield _MessagingBus.ensureStream(jsm, streamName, streamSubjects);
|
|
220
|
+
try {
|
|
221
|
+
yield jsm.consumers.info(streamName, durableName);
|
|
222
|
+
} catch (e) {
|
|
223
|
+
try {
|
|
224
|
+
yield jsm.consumers.add(streamName, {
|
|
225
|
+
durable_name: durableName,
|
|
226
|
+
ack_policy: import_nats.AckPolicy.Explicit,
|
|
227
|
+
max_deliver: maxDeliver,
|
|
228
|
+
ack_wait: 60 * 1e9
|
|
229
|
+
// 60s
|
|
230
|
+
});
|
|
231
|
+
} catch (e2) {
|
|
232
|
+
yield jsm.consumers.info(streamName, durableName);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const consumer = yield nc.jetstream().consumers.get(streamName, durableName);
|
|
236
|
+
const messages = yield consumer.consume({ max_messages: 1 });
|
|
237
|
+
const sc = (0, import_nats.JSONCodec)();
|
|
238
|
+
(() => __async(this, null, function* () {
|
|
239
|
+
try {
|
|
240
|
+
for (var iter = __forAwait(messages), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
|
|
241
|
+
const m = temp.value;
|
|
242
|
+
try {
|
|
243
|
+
yield onMessage(sc.decode(m.data));
|
|
244
|
+
m.ack();
|
|
245
|
+
} catch (err) {
|
|
246
|
+
console.error(`subscribeDurable(${channel}) handler error \u2014 will redeliver:`, err);
|
|
247
|
+
m.nak();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} catch (temp) {
|
|
251
|
+
error = [temp];
|
|
252
|
+
} finally {
|
|
253
|
+
try {
|
|
254
|
+
more && (temp = iter.return) && (yield temp.call(iter));
|
|
255
|
+
} finally {
|
|
256
|
+
if (error)
|
|
257
|
+
throw error[0];
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}))().catch((err) => console.error(`subscribeDurable(${channel}) consume loop ended:`, err));
|
|
261
|
+
console.log(`durably listening for ${channel} via ${streamName}/${durableName}`);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
154
264
|
};
|
|
155
265
|
|
|
156
266
|
// src/notifications.ts
|
|
@@ -307,10 +417,24 @@ var MiaShifts = class {
|
|
|
307
417
|
};
|
|
308
418
|
|
|
309
419
|
// src/critical-errors.ts
|
|
420
|
+
var CRITICAL_ERRORS_STREAM = "CRITICAL_ERRORS";
|
|
421
|
+
var CRITICAL_ERRORS_SUBJECT = "mia.errors.critical";
|
|
310
422
|
var MiaCriticalErrors = class {
|
|
423
|
+
/**
|
|
424
|
+
* Publish a critical-error alert durably. Resolves only once the alert is
|
|
425
|
+
* persisted to the CRITICAL_ERRORS JetStream stream, so a resolved call
|
|
426
|
+
* guarantees the alert survives even if the notifications service is down —
|
|
427
|
+
* it is replayed to that durable consumer on recovery. Callers may therefore
|
|
428
|
+
* treat a resolved publish as "the operator will be notified".
|
|
429
|
+
*/
|
|
311
430
|
static publish(event) {
|
|
312
431
|
return __async(this, null, function* () {
|
|
313
|
-
yield MessagingBus.
|
|
432
|
+
yield MessagingBus.publishDurable(
|
|
433
|
+
CRITICAL_ERRORS_SUBJECT,
|
|
434
|
+
event,
|
|
435
|
+
CRITICAL_ERRORS_STREAM,
|
|
436
|
+
["mia.errors.*"]
|
|
437
|
+
);
|
|
314
438
|
});
|
|
315
439
|
}
|
|
316
440
|
};
|
|
@@ -350,6 +474,8 @@ var MiaExpenses = class {
|
|
|
350
474
|
};
|
|
351
475
|
// Annotate the CommonJS export names for ESM import in node:
|
|
352
476
|
0 && (module.exports = {
|
|
477
|
+
CRITICAL_ERRORS_STREAM,
|
|
478
|
+
CRITICAL_ERRORS_SUBJECT,
|
|
353
479
|
MessagingBus,
|
|
354
480
|
MiaAuditTrace,
|
|
355
481
|
MiaCriticalErrors,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/mia-server-response.ts","../src/messaging-bus.ts","../src/notifications.ts","../src/customers.ts","../src/multilanguage.ts","../src/order-attributes.ts","../src/orders.ts","../src/shifts.ts","../src/critical-errors.ts","../src/audit-trace.ts","../src/stock.ts","../src/expenses.ts"],"sourcesContent":["export * from './mia-server-response'\nexport * from './notifications'\nexport * from './messaging-bus'\nexport * from './customers'\nexport * from './multilanguage'\nexport * from './order-attributes'\nexport * from './orders'\nexport * from './shifts'\nexport * from './critical-errors'\nexport * from './audit-trace'\nexport * from './stock'\nexport * from './expenses'\n","export class MiaServerResponse {\n status?: number;\n message?: string;\n data: any;\n\n static send = (status: number, message: string, data: any) => {\n const response: MiaServerResponse = {\n status: status,\n message: message,\n data: data\n };\n return response;\n }\n static sendData = (data: any, status: number = 200) => {\n const response: MiaServerResponse = {\n status: status,\n message: \"Success\",\n data: data\n };\n return response;\n }\n static sendError = (error: any, status: number = 400) => {\n const response: MiaServerResponse = {\n status: status,\n message: \"Failed\",\n data: error\n };\n return response;\n }\n}\n","import { connect, JSONCodec } from \"nats\";\n\nexport class MessagingBus {\n static async subscribe(channel: string, queue: string, onMessage: Function) {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n const sc = JSONCodec();\n const subscription = nc.subscribe(channel, { queue: queue });\n\n (async (sub) => {\n\n console.log(`listening for ${sub.getSubject()} requests...`);\n\n for await (const m of sub) {\n const msg = sc.decode(m.data);\n onMessage(msg);\n }\n\n console.log(`subscription ${sub.getSubject()} drained.`);\n })(subscription);\n }\n\n static async publish(channel: string, message: any) {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n const jc = JSONCodec();\n nc.publish(channel, jc.encode(message));\n await nc.drain();\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\nexport interface Notification {\n title?: string,\n body: string,\n badge?: number,\n payload?: any,\n target?: string\n}\nexport interface UserNotification extends Notification {\n userId: string\n}\nexport interface DeviceNotification extends Notification {\n deviceId: string\n}\nexport interface TopicNotification extends Notification {\n topic: string,\n}\nexport interface SmsNotification extends Notification {\n mobile: string,\n}\nexport interface StaffNotification extends Notification {\n staffId: string\n brandId: string\n}\n\nexport interface PrintLine {\n text: string,\n alignment: 'left' | 'center' | 'right',\n size: 'normal' | 'large' | 'small',\n weight: 'normal' | 'bold'\n}\n\nexport interface OperateOrderNotificationPayload {\n orderId: string,\n serveTo: string | null,\n items: Array<{\n name: string,\n quantity: number,\n price: number,\n description: string | null\n }>,\n notes: string | null,\n lines: PrintLine[]\n}\n\nexport interface OperateOrderNotification {\n brandId: string,\n branchId: string,\n title: string,\n body: string,\n order: OperateOrderNotificationPayload\n}\n\nexport class MiaNotifications {\n static async sendtoDevice(message: DeviceNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoUserDevices(message: UserNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoTopic(message: TopicNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoSms(message: SmsNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoStaff(message: StaffNotification) {\n const topic = MiaNotifications.staffTopicFormatter(message.brandId, message.staffId);\n await MiaNotifications.sendtoTopic({\n topic,\n title: message.title,\n body: message.body,\n badge: message.badge,\n payload: message.payload,\n target: message.target\n });\n }\n static async sendOperateOrderNotification(message: OperateOrderNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, {\n ...message,\n type: 'operateOrder'\n });\n }\n static topicFormatter = (topic: string) => \"/topics/\" + topic.replace(/\\//g, \"~\")\n static operateOrderTopicFormatter = (brandId: string, branchId: string) =>\n `~operate~orders~${brandId}~${branchId}`\n static staffTopicFormatter = (brandId: string, staffId: string) =>\n `/topics/~staff~${brandId}~${staffId}`\n}","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface CustomerMobileVerifiedMessage {\n brandId: string;\n customer: any;\n isVerified: boolean;\n}\n\nexport interface CustomerActionMessage {\n brandId: string;\n type: string;\n customer: any;\n payload: any;\n}\n\nexport interface CustomerAddTagMessage {\n brandId: string;\n customer: any;\n tag: string;\n remove: boolean;\n}\n\nexport interface CustomerUpdateNotesMessage {\n brandId: string;\n customer: any;\n newNotes: string;\n}\n\nexport class MicrogemsCustomer {\n static async mobileVerified(message: CustomerMobileVerifiedMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async action(message: CustomerActionMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async addTag(message: CustomerAddTagMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async updateNotes(message: CustomerUpdateNotesMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n}","/**\n * Multilanguage String Type\n *\n * Represents text content that can be displayed in multiple languages.\n * Used across services for internationalization support.\n */\n\nexport interface MultilanguageString {\n /**\n * English text (required)\n */\n en: string;\n\n /**\n * Arabic text (optional)\n */\n ar?: string;\n}\n\n/**\n * Helper function to get text in preferred language with fallback\n *\n * @param text - Multilanguage string object\n * @param language - Preferred language ('en' | 'ar')\n * @returns Text in preferred language, or English if not available\n */\nexport function getLocalizedText(\n text: MultilanguageString,\n language: 'en' | 'ar' = 'en'\n): string {\n if (language === 'ar' && text.ar) {\n return text.ar;\n }\n return text.en;\n}\n\n/**\n * Helper function to create a multilanguage string from a plain string\n *\n * @param enText - English text\n * @param arText - Optional Arabic text\n * @returns MultilanguageString object\n */\nexport function createMultilanguageString(\n enText: string,\n arText?: string\n): MultilanguageString {\n const result: MultilanguageString = { en: enText };\n if (arText) {\n result.ar = arText;\n }\n return result;\n}\n","/**\n * Order Item Attributes\n *\n * Shared types for order item customization attributes used across:\n * - menu API (for menu item attributes)\n * - schedule API (for appointment service attributes)\n * - transactions API (for order item attributes)\n */\n\n/**\n * Single attribute value option\n */\nexport interface AttributeValue {\n /**\n * Unique identifier for this value option\n */\n id: string;\n\n /**\n * Display name of the value\n */\n name: string;\n\n /**\n * Additional price for selecting this value (0 if no extra cost)\n */\n price: number;\n\n /**\n * Whether this value is available/in-stock\n */\n isAvailable?: boolean;\n}\n\n/**\n * Attribute definition (e.g., \"Size\", \"Color\", \"Extras\")\n */\nexport interface OrderAttribute {\n /**\n * Unique identifier for this attribute\n */\n id: string;\n\n /**\n * Attribute name (e.g., \"Size\", \"Toppings\")\n */\n name: string;\n\n /**\n * Available values for this attribute\n */\n values: AttributeValue[];\n\n /**\n * Whether customer must select a value for this attribute\n */\n required?: boolean;\n\n /**\n * Allow multiple values to be selected (e.g., multiple toppings)\n */\n allowMultiple?: boolean;\n\n /**\n * Minimum number of selections (if allowMultiple = true)\n */\n minSelections?: number;\n\n /**\n * Maximum number of selections (if allowMultiple = true)\n */\n maxSelections?: number;\n}\n\n/**\n * Selected attribute for an order item\n * (What the customer actually selected)\n */\nexport interface SelectedOrderAttribute {\n /**\n * Reference to the attribute definition\n */\n attributeId: string;\n\n /**\n * Attribute name (denormalized for display)\n */\n attributeName: string;\n\n /**\n * Selected value(s)\n */\n selectedValues: {\n /**\n * Value ID\n */\n id: string;\n\n /**\n * Value name (denormalized for display)\n */\n name: string;\n\n /**\n * Price of this selected value\n */\n price: number;\n }[];\n\n /**\n * Total price for this attribute (sum of all selected values)\n */\n totalPrice: number;\n}\n\n/**\n * Array of selected attributes for an order item\n * This is what gets stored in Appointment.orderItemAttributes or Order.items[].attributes\n */\nexport type OrderItemAttributes = SelectedOrderAttribute[];\n\n/**\n * Helper function to calculate total price from selected attributes\n *\n * @param attributes - Array of selected attributes\n * @returns Total additional price from all attributes\n */\nexport function calculateAttributesPrice(\n attributes: OrderItemAttributes\n): number {\n return attributes.reduce((total, attr) => total + attr.totalPrice, 0);\n}\n\n/**\n * Helper function to validate selected attributes against definitions\n *\n * @param selected - Selected attributes\n * @param definitions - Attribute definitions\n * @returns Validation result with any errors\n */\nexport function validateSelectedAttributes(\n selected: OrderItemAttributes,\n definitions: OrderAttribute[]\n): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check required attributes\n for (const def of definitions) {\n if (def.required) {\n const selectedAttr = selected.find((s) => s.attributeId === def.id);\n if (!selectedAttr || selectedAttr.selectedValues.length === 0) {\n errors.push(`Attribute \"${def.name}\" is required`);\n }\n }\n }\n\n // Check selection counts\n for (const selectedAttr of selected) {\n const def = definitions.find((d) => d.id === selectedAttr.attributeId);\n if (!def) {\n errors.push(`Unknown attribute: ${selectedAttr.attributeName}`);\n continue;\n }\n\n const count = selectedAttr.selectedValues.length;\n\n if (!def.allowMultiple && count > 1) {\n errors.push(`Attribute \"${def.name}\" allows only one selection`);\n }\n\n if (def.minSelections && count < def.minSelections) {\n errors.push(\n `Attribute \"${def.name}\" requires at least ${def.minSelections} selections`\n );\n }\n\n if (def.maxSelections && count > def.maxSelections) {\n errors.push(\n `Attribute \"${def.name}\" allows at most ${def.maxSelections} selections`\n );\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport type ServiceType = 'MENU' | 'APPOINTMENT' | 'CREDIT_BUNDLE';\n\n// ZATCA-compliant buyer identification schemes for BR-KSA-81\nexport type BuyerIdentificationScheme = 'TIN'|'CRN'|'MOM'|'MLS'|'700'|'SAG'|'NAT'|'GCC'|'IQA'|'PAS'|'OTH';\n\nexport interface OrderItemForInvoice {\n idInService: string;\n name: string;\n description?: string;\n price: number;\n quantity: number;\n vatRate?: number;\n invoicable?: boolean; // defaults to true; set to false for items already paid (e.g., package redemptions)\n orderAttributes?: { id?: string; name: string; price: number; quantity?: number }[];\n}\n\nexport interface PaymentInfo {\n type: string;\n amount: number;\n reference?: string;\n}\n\n// Fee information for ZATCA invoice charges\nexport interface OrderFeeInfo {\n feeId?: string;\n name: string;\n type: string;\n value: number;\n calculatedAmount: number; // VAT-inclusive amount\n}\n\nexport interface CustomerInfo {\n name?: string;\n phoneNumber?: string;\n countryCode?: string;\n email?: string;\n vatNumber?: string;\n crNumber?: string;\n streetName?: string;\n buildingNumber?: string;\n cityName?: string;\n postalZone?: string;\n district?: string;\n identificationNumber?: string;\n identificationScheme?: BuyerIdentificationScheme;\n // B2C (default when undefined) vs B2B. Drives ZATCA Standard vs Simplified\n // selection downstream in the invoice service.\n kind?: 'B2C' | 'B2B';\n}\n\nexport interface OrderCreatedEvent {\n orderId: string;\n brandId: string;\n branchId?: string;\n source: ServiceType;\n subtotal: number;\n vatAmount: number;\n vatPercentage?: number;\n total: number;\n items: OrderItemForInvoice[];\n payments: PaymentInfo[];\n customerInfo?: CustomerInfo;\n dailyOrderNumber?: number;\n yearlyOrderNumber?: number;\n createdAt: Date;\n additionalFees?: OrderFeeInfo[]; // Order-level fees (minimum order, service, delivery, etc.)\n}\n\nexport class MiaOrders {\n static async publishOrderCreated(event: OrderCreatedEvent): Promise<void> {\n await MessagingBus.publish('mia.orders.created', event);\n }\n}\n\nexport interface PreorderPaymentEvent {\n preorderId: string;\n sourceId: string;\n source: ServiceType;\n brandId: string;\n branchId?: string;\n payment: PaymentInfo;\n totalPaid: number;\n total: number;\n isFullyPaid: boolean;\n orderId?: string;\n}\n\nexport class MiaPreorders {\n static async publishPaymentAdded(event: PreorderPaymentEvent): Promise<void> {\n await MessagingBus.publish('mia.preorders.payment-added', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface ShiftClosedEvent {\n shiftId: string;\n brandId: string;\n branchId: string;\n staffId: string;\n openedAt: string;\n closedAt: string;\n initialCash: number;\n expectedCash: number;\n closingCash: number;\n paymentSummary: Array<{ type: string; total: number; count: number }>;\n refundSummary: Array<{ type: string; total: number; count: number }>;\n totalRefunds: number;\n closingNotes?: string;\n staffName?: string;\n}\n\nexport class MiaShifts {\n static async publishShiftClosed(event: ShiftClosedEvent): Promise<void> {\n await MessagingBus.publish('mia.shifts.closed', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface CriticalErrorEvent {\n brandId: string;\n branchId?: string;\n service: string;\n errorType: string;\n summary: string;\n details: string;\n resourceId?: string;\n resourceType?: string;\n occurredAt: string;\n}\n\nexport class MiaCriticalErrors {\n static async publish(event: CriticalErrorEvent): Promise<void> {\n await MessagingBus.publish('mia.errors.critical', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport type AuditRiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\n\nexport interface AuditTraceEvent {\n action: string;\n riskLevel: AuditRiskLevel;\n service: string;\n staffId?: string;\n staffRole?: string;\n brandId: string;\n branchId?: string;\n financialImpact?: {\n amount: number;\n currency: string;\n };\n objectRef?: {\n collection: string;\n id: string;\n snapshot?: Record<string, any>;\n };\n details?: string;\n metadata?: Record<string, any>;\n performedAt?: string;\n}\n\nexport class MiaAuditTrace {\n static async publish(event: AuditTraceEvent): Promise<void> {\n await MessagingBus.publish('mia.audit.trace', {\n ...event,\n performedAt: event.performedAt || new Date().toISOString(),\n });\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface LowStockEvent {\n brandId: string;\n branchId: string;\n rawMaterialId: string;\n materialName: string;\n currentQuantity: number;\n lowThreshold: number;\n unit: string;\n}\n\nexport type StockTransactionType =\n | 'ORDER_DEDUCTION'\n | 'MANUAL_ADJUSTMENT'\n | 'SUPPLIER_RECEIPT'\n | 'TRANSFER_IN'\n | 'TRANSFER_OUT'\n | 'PRODUCTION_INPUT'\n | 'PRODUCTION_OUTPUT';\n\nexport type RawMaterialKind = 'SIMPLE' | 'COMPOSITE';\n\nexport type PurchaseOrderStatus =\n | 'DRAFT'\n | 'ORDERED'\n | 'PARTIALLY_RECEIVED'\n | 'RECEIVED'\n | 'CANCELLED';\n\nexport type CustomerKind = 'B2C' | 'B2B';\nexport type CustomerRole = 'CUSTOMER' | 'SUPPLIER';\n\nexport class MiaStock {\n static async publishLowStockAlert(event: LowStockEvent): Promise<void> {\n await MessagingBus.publish('mia.menu.stock.low', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface PurchaseOrderRecordedEvent {\n poId: string;\n brandId: string;\n branchId?: string;\n supplierCustomerId?: string;\n poNumber?: string;\n // Cash basis (inc-VAT) total in SAR; consumers use this directly as amount.\n amountIncVat: number;\n // Convenience extra in case a consumer wants the pre-tax figure.\n amountExVat?: number;\n occurredAt: string; // ISO string\n // Actor that ordered the PO. `staffId` set when ordered by staff, otherwise\n // `userId` is set (e.g. brand owner). Consumers map to their own actor shape.\n staffId?: string;\n userId?: string;\n}\n\nexport interface PurchaseOrderCancelledEvent {\n poId: string;\n brandId: string;\n}\n\nexport class MiaExpenses {\n static async publishPurchaseOrderRecorded(event: PurchaseOrderRecordedEvent): Promise<void> {\n await MessagingBus.publish('mia.expenses.purchase-order.recorded', event);\n }\n\n static async publishPurchaseOrderCancelled(event: PurchaseOrderCancelledEvent): Promise<void> {\n await MessagingBus.publish('mia.expenses.purchase-order.cancelled', event);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/mia-server-response.ts","../src/messaging-bus.ts","../src/notifications.ts","../src/customers.ts","../src/multilanguage.ts","../src/order-attributes.ts","../src/orders.ts","../src/shifts.ts","../src/critical-errors.ts","../src/audit-trace.ts","../src/stock.ts","../src/expenses.ts"],"sourcesContent":["export * from './mia-server-response'\nexport * from './notifications'\nexport * from './messaging-bus'\nexport * from './customers'\nexport * from './multilanguage'\nexport * from './order-attributes'\nexport * from './orders'\nexport * from './shifts'\nexport * from './critical-errors'\nexport * from './audit-trace'\nexport * from './stock'\nexport * from './expenses'\n","export class MiaServerResponse {\n status?: number;\n message?: string;\n data: any;\n\n static send = (status: number, message: string, data: any) => {\n const response: MiaServerResponse = {\n status: status,\n message: message,\n data: data\n };\n return response;\n }\n static sendData = (data: any, status: number = 200) => {\n const response: MiaServerResponse = {\n status: status,\n message: \"Success\",\n data: data\n };\n return response;\n }\n static sendError = (error: any, status: number = 400) => {\n const response: MiaServerResponse = {\n status: status,\n message: \"Failed\",\n data: error\n };\n return response;\n }\n}\n","import { connect, JSONCodec, StorageType, AckPolicy } from \"nats\";\n\nconst SEVEN_DAYS_NS = 7 * 24 * 60 * 60 * 1_000_000_000;\n\nexport class MessagingBus {\n static async subscribe(channel: string, queue: string, onMessage: Function) {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n const sc = JSONCodec();\n const subscription = nc.subscribe(channel, { queue: queue });\n\n (async (sub) => {\n\n console.log(`listening for ${sub.getSubject()} requests...`);\n\n for await (const m of sub) {\n const msg = sc.decode(m.data);\n onMessage(msg);\n }\n\n console.log(`subscription ${sub.getSubject()} drained.`);\n })(subscription);\n }\n\n static async publish(channel: string, message: any) {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n const jc = JSONCodec();\n nc.publish(channel, jc.encode(message));\n await nc.drain();\n }\n\n /**\n * Idempotently ensure a JetStream stream exists for `streamSubjects`.\n * Tolerates create races between concurrent callers.\n */\n private static async ensureStream(\n jsm: any,\n streamName: string,\n streamSubjects: string[]\n ): Promise<void> {\n try {\n await jsm.streams.info(streamName);\n } catch {\n try {\n await jsm.streams.add({\n name: streamName,\n subjects: streamSubjects,\n storage: StorageType.File,\n max_age: SEVEN_DAYS_NS,\n });\n } catch {\n // Another instance may have created it between info() and add().\n await jsm.streams.info(streamName); // throws only if it truly doesn't exist\n }\n }\n }\n\n /**\n * Durable publish: persists the message to a JetStream stream and resolves\n * ONLY after the server acknowledges the store. Unlike `publish` (fire-and-\n * forget core NATS), a resolved `publishDurable` means the message survives\n * even when no subscriber is currently connected — use it for alerts/events\n * that must never be lost while a consumer is down.\n */\n static async publishDurable(\n channel: string,\n message: any,\n streamName: string,\n streamSubjects: string[] = [channel]\n ): Promise<void> {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n try {\n const jsm = await nc.jetstreamManager();\n await MessagingBus.ensureStream(jsm, streamName, streamSubjects);\n const jc = JSONCodec();\n // js.publish awaits the PubAck — it rejects if the store fails.\n await nc.jetstream().publish(channel, jc.encode(message));\n } finally {\n await nc.drain();\n }\n }\n\n /**\n * Durable subscribe: binds a durable JetStream consumer so messages\n * published while this service was down are REPLAYED when it reconnects\n * (unlike `subscribe`, which only delivers live messages). Each message is\n * acked after the handler resolves; a handler that throws naks for redelivery\n * (bounded by maxDeliver so a poison message can't loop forever).\n */\n static async subscribeDurable(\n channel: string,\n streamName: string,\n durableName: string,\n onMessage: (message: any) => Promise<void> | void,\n options: { streamSubjects?: string[]; maxDeliver?: number } = {}\n ): Promise<void> {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const streamSubjects = options.streamSubjects ?? [channel];\n const maxDeliver = options.maxDeliver ?? 10;\n\n const nc = await connect({ servers: process.env.NATS_HOST });\n const jsm = await nc.jetstreamManager();\n await MessagingBus.ensureStream(jsm, streamName, streamSubjects);\n\n try {\n await jsm.consumers.info(streamName, durableName);\n } catch {\n try {\n await jsm.consumers.add(streamName, {\n durable_name: durableName,\n ack_policy: AckPolicy.Explicit,\n max_deliver: maxDeliver,\n ack_wait: 60 * 1_000_000_000, // 60s\n });\n } catch {\n await jsm.consumers.info(streamName, durableName); // lost create race; ok if it now exists\n }\n }\n\n const consumer = await nc.jetstream().consumers.get(streamName, durableName);\n const messages = await consumer.consume({ max_messages: 1 });\n const sc = JSONCodec();\n (async () => {\n for await (const m of messages) {\n try {\n await onMessage(sc.decode(m.data));\n m.ack();\n } catch (err) {\n console.error(`subscribeDurable(${channel}) handler error — will redeliver:`, err);\n m.nak();\n }\n }\n })().catch((err) => console.error(`subscribeDurable(${channel}) consume loop ended:`, err));\n\n console.log(`durably listening for ${channel} via ${streamName}/${durableName}`);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\nexport interface Notification {\n title?: string,\n body: string,\n badge?: number,\n payload?: any,\n target?: string\n}\nexport interface UserNotification extends Notification {\n userId: string\n}\nexport interface DeviceNotification extends Notification {\n deviceId: string\n}\nexport interface TopicNotification extends Notification {\n topic: string,\n}\nexport interface SmsNotification extends Notification {\n mobile: string,\n}\nexport interface StaffNotification extends Notification {\n staffId: string\n brandId: string\n}\n\nexport interface PrintLine {\n text: string,\n alignment: 'left' | 'center' | 'right',\n size: 'normal' | 'large' | 'small',\n weight: 'normal' | 'bold'\n}\n\nexport interface OperateOrderNotificationPayload {\n orderId: string,\n serveTo: string | null,\n items: Array<{\n name: string,\n quantity: number,\n price: number,\n description: string | null\n }>,\n notes: string | null,\n lines: PrintLine[]\n}\n\nexport interface OperateOrderNotification {\n brandId: string,\n branchId: string,\n title: string,\n body: string,\n order: OperateOrderNotificationPayload\n}\n\nexport class MiaNotifications {\n static async sendtoDevice(message: DeviceNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoUserDevices(message: UserNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoTopic(message: TopicNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoSms(message: SmsNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoStaff(message: StaffNotification) {\n const topic = MiaNotifications.staffTopicFormatter(message.brandId, message.staffId);\n await MiaNotifications.sendtoTopic({\n topic,\n title: message.title,\n body: message.body,\n badge: message.badge,\n payload: message.payload,\n target: message.target\n });\n }\n static async sendOperateOrderNotification(message: OperateOrderNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, {\n ...message,\n type: 'operateOrder'\n });\n }\n static topicFormatter = (topic: string) => \"/topics/\" + topic.replace(/\\//g, \"~\")\n static operateOrderTopicFormatter = (brandId: string, branchId: string) =>\n `~operate~orders~${brandId}~${branchId}`\n static staffTopicFormatter = (brandId: string, staffId: string) =>\n `/topics/~staff~${brandId}~${staffId}`\n}","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface CustomerMobileVerifiedMessage {\n brandId: string;\n customer: any;\n isVerified: boolean;\n}\n\nexport interface CustomerActionMessage {\n brandId: string;\n type: string;\n customer: any;\n payload: any;\n}\n\nexport interface CustomerAddTagMessage {\n brandId: string;\n customer: any;\n tag: string;\n remove: boolean;\n}\n\nexport interface CustomerUpdateNotesMessage {\n brandId: string;\n customer: any;\n newNotes: string;\n}\n\nexport class MicrogemsCustomer {\n static async mobileVerified(message: CustomerMobileVerifiedMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async action(message: CustomerActionMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async addTag(message: CustomerAddTagMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async updateNotes(message: CustomerUpdateNotesMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n}","/**\n * Multilanguage String Type\n *\n * Represents text content that can be displayed in multiple languages.\n * Used across services for internationalization support.\n */\n\nexport interface MultilanguageString {\n /**\n * English text (required)\n */\n en: string;\n\n /**\n * Arabic text (optional)\n */\n ar?: string;\n}\n\n/**\n * Helper function to get text in preferred language with fallback\n *\n * @param text - Multilanguage string object\n * @param language - Preferred language ('en' | 'ar')\n * @returns Text in preferred language, or English if not available\n */\nexport function getLocalizedText(\n text: MultilanguageString,\n language: 'en' | 'ar' = 'en'\n): string {\n if (language === 'ar' && text.ar) {\n return text.ar;\n }\n return text.en;\n}\n\n/**\n * Helper function to create a multilanguage string from a plain string\n *\n * @param enText - English text\n * @param arText - Optional Arabic text\n * @returns MultilanguageString object\n */\nexport function createMultilanguageString(\n enText: string,\n arText?: string\n): MultilanguageString {\n const result: MultilanguageString = { en: enText };\n if (arText) {\n result.ar = arText;\n }\n return result;\n}\n","/**\n * Order Item Attributes\n *\n * Shared types for order item customization attributes used across:\n * - menu API (for menu item attributes)\n * - schedule API (for appointment service attributes)\n * - transactions API (for order item attributes)\n */\n\n/**\n * Single attribute value option\n */\nexport interface AttributeValue {\n /**\n * Unique identifier for this value option\n */\n id: string;\n\n /**\n * Display name of the value\n */\n name: string;\n\n /**\n * Additional price for selecting this value (0 if no extra cost)\n */\n price: number;\n\n /**\n * Whether this value is available/in-stock\n */\n isAvailable?: boolean;\n}\n\n/**\n * Attribute definition (e.g., \"Size\", \"Color\", \"Extras\")\n */\nexport interface OrderAttribute {\n /**\n * Unique identifier for this attribute\n */\n id: string;\n\n /**\n * Attribute name (e.g., \"Size\", \"Toppings\")\n */\n name: string;\n\n /**\n * Available values for this attribute\n */\n values: AttributeValue[];\n\n /**\n * Whether customer must select a value for this attribute\n */\n required?: boolean;\n\n /**\n * Allow multiple values to be selected (e.g., multiple toppings)\n */\n allowMultiple?: boolean;\n\n /**\n * Minimum number of selections (if allowMultiple = true)\n */\n minSelections?: number;\n\n /**\n * Maximum number of selections (if allowMultiple = true)\n */\n maxSelections?: number;\n}\n\n/**\n * Selected attribute for an order item\n * (What the customer actually selected)\n */\nexport interface SelectedOrderAttribute {\n /**\n * Reference to the attribute definition\n */\n attributeId: string;\n\n /**\n * Attribute name (denormalized for display)\n */\n attributeName: string;\n\n /**\n * Selected value(s)\n */\n selectedValues: {\n /**\n * Value ID\n */\n id: string;\n\n /**\n * Value name (denormalized for display)\n */\n name: string;\n\n /**\n * Price of this selected value\n */\n price: number;\n }[];\n\n /**\n * Total price for this attribute (sum of all selected values)\n */\n totalPrice: number;\n}\n\n/**\n * Array of selected attributes for an order item\n * This is what gets stored in Appointment.orderItemAttributes or Order.items[].attributes\n */\nexport type OrderItemAttributes = SelectedOrderAttribute[];\n\n/**\n * Helper function to calculate total price from selected attributes\n *\n * @param attributes - Array of selected attributes\n * @returns Total additional price from all attributes\n */\nexport function calculateAttributesPrice(\n attributes: OrderItemAttributes\n): number {\n return attributes.reduce((total, attr) => total + attr.totalPrice, 0);\n}\n\n/**\n * Helper function to validate selected attributes against definitions\n *\n * @param selected - Selected attributes\n * @param definitions - Attribute definitions\n * @returns Validation result with any errors\n */\nexport function validateSelectedAttributes(\n selected: OrderItemAttributes,\n definitions: OrderAttribute[]\n): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check required attributes\n for (const def of definitions) {\n if (def.required) {\n const selectedAttr = selected.find((s) => s.attributeId === def.id);\n if (!selectedAttr || selectedAttr.selectedValues.length === 0) {\n errors.push(`Attribute \"${def.name}\" is required`);\n }\n }\n }\n\n // Check selection counts\n for (const selectedAttr of selected) {\n const def = definitions.find((d) => d.id === selectedAttr.attributeId);\n if (!def) {\n errors.push(`Unknown attribute: ${selectedAttr.attributeName}`);\n continue;\n }\n\n const count = selectedAttr.selectedValues.length;\n\n if (!def.allowMultiple && count > 1) {\n errors.push(`Attribute \"${def.name}\" allows only one selection`);\n }\n\n if (def.minSelections && count < def.minSelections) {\n errors.push(\n `Attribute \"${def.name}\" requires at least ${def.minSelections} selections`\n );\n }\n\n if (def.maxSelections && count > def.maxSelections) {\n errors.push(\n `Attribute \"${def.name}\" allows at most ${def.maxSelections} selections`\n );\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport type ServiceType = 'MENU' | 'APPOINTMENT' | 'CREDIT_BUNDLE';\n\n// ZATCA-compliant buyer identification schemes for BR-KSA-81\nexport type BuyerIdentificationScheme = 'TIN'|'CRN'|'MOM'|'MLS'|'700'|'SAG'|'NAT'|'GCC'|'IQA'|'PAS'|'OTH';\n\nexport interface OrderItemForInvoice {\n idInService: string;\n name: string;\n description?: string;\n price: number;\n quantity: number;\n vatRate?: number;\n invoicable?: boolean; // defaults to true; set to false for items already paid (e.g., package redemptions)\n orderAttributes?: { id?: string; name: string; price: number; quantity?: number }[];\n}\n\nexport interface PaymentInfo {\n type: string;\n amount: number;\n reference?: string;\n}\n\n// Fee information for ZATCA invoice charges\nexport interface OrderFeeInfo {\n feeId?: string;\n name: string;\n type: string;\n value: number;\n calculatedAmount: number; // VAT-inclusive amount\n}\n\nexport interface CustomerInfo {\n name?: string;\n phoneNumber?: string;\n countryCode?: string;\n email?: string;\n vatNumber?: string;\n crNumber?: string;\n streetName?: string;\n buildingNumber?: string;\n cityName?: string;\n postalZone?: string;\n district?: string;\n identificationNumber?: string;\n identificationScheme?: BuyerIdentificationScheme;\n // B2C (default when undefined) vs B2B. Drives ZATCA Standard vs Simplified\n // selection downstream in the invoice service.\n kind?: 'B2C' | 'B2B';\n}\n\nexport interface OrderCreatedEvent {\n orderId: string;\n brandId: string;\n branchId?: string;\n source: ServiceType;\n subtotal: number;\n vatAmount: number;\n vatPercentage?: number;\n total: number;\n items: OrderItemForInvoice[];\n payments: PaymentInfo[];\n customerInfo?: CustomerInfo;\n dailyOrderNumber?: number;\n yearlyOrderNumber?: number;\n createdAt: Date;\n additionalFees?: OrderFeeInfo[]; // Order-level fees (minimum order, service, delivery, etc.)\n}\n\nexport class MiaOrders {\n static async publishOrderCreated(event: OrderCreatedEvent): Promise<void> {\n await MessagingBus.publish('mia.orders.created', event);\n }\n}\n\nexport interface PreorderPaymentEvent {\n preorderId: string;\n sourceId: string;\n source: ServiceType;\n brandId: string;\n branchId?: string;\n payment: PaymentInfo;\n totalPaid: number;\n total: number;\n isFullyPaid: boolean;\n orderId?: string;\n}\n\nexport class MiaPreorders {\n static async publishPaymentAdded(event: PreorderPaymentEvent): Promise<void> {\n await MessagingBus.publish('mia.preorders.payment-added', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface ShiftClosedEvent {\n shiftId: string;\n brandId: string;\n branchId: string;\n staffId: string;\n openedAt: string;\n closedAt: string;\n initialCash: number;\n expectedCash: number;\n closingCash: number;\n paymentSummary: Array<{ type: string; total: number; count: number }>;\n refundSummary: Array<{ type: string; total: number; count: number }>;\n totalRefunds: number;\n closingNotes?: string;\n staffName?: string;\n}\n\nexport class MiaShifts {\n static async publishShiftClosed(event: ShiftClosedEvent): Promise<void> {\n await MessagingBus.publish('mia.shifts.closed', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface CriticalErrorEvent {\n brandId: string;\n branchId?: string;\n service: string;\n errorType: string;\n summary: string;\n details: string;\n resourceId?: string;\n resourceType?: string;\n occurredAt: string;\n}\n\nexport const CRITICAL_ERRORS_STREAM = 'CRITICAL_ERRORS';\nexport const CRITICAL_ERRORS_SUBJECT = 'mia.errors.critical';\n\nexport class MiaCriticalErrors {\n /**\n * Publish a critical-error alert durably. Resolves only once the alert is\n * persisted to the CRITICAL_ERRORS JetStream stream, so a resolved call\n * guarantees the alert survives even if the notifications service is down —\n * it is replayed to that durable consumer on recovery. Callers may therefore\n * treat a resolved publish as \"the operator will be notified\".\n */\n static async publish(event: CriticalErrorEvent): Promise<void> {\n await MessagingBus.publishDurable(\n CRITICAL_ERRORS_SUBJECT,\n event,\n CRITICAL_ERRORS_STREAM,\n ['mia.errors.*']\n );\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport type AuditRiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\n\nexport interface AuditTraceEvent {\n action: string;\n riskLevel: AuditRiskLevel;\n service: string;\n staffId?: string;\n staffRole?: string;\n brandId: string;\n branchId?: string;\n financialImpact?: {\n amount: number;\n currency: string;\n };\n objectRef?: {\n collection: string;\n id: string;\n snapshot?: Record<string, any>;\n };\n details?: string;\n metadata?: Record<string, any>;\n performedAt?: string;\n}\n\nexport class MiaAuditTrace {\n static async publish(event: AuditTraceEvent): Promise<void> {\n await MessagingBus.publish('mia.audit.trace', {\n ...event,\n performedAt: event.performedAt || new Date().toISOString(),\n });\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface LowStockEvent {\n brandId: string;\n branchId: string;\n rawMaterialId: string;\n materialName: string;\n currentQuantity: number;\n lowThreshold: number;\n unit: string;\n}\n\nexport type StockTransactionType =\n | 'ORDER_DEDUCTION'\n | 'MANUAL_ADJUSTMENT'\n | 'SUPPLIER_RECEIPT'\n | 'TRANSFER_IN'\n | 'TRANSFER_OUT'\n | 'PRODUCTION_INPUT'\n | 'PRODUCTION_OUTPUT';\n\nexport type RawMaterialKind = 'SIMPLE' | 'COMPOSITE';\n\nexport type PurchaseOrderStatus =\n | 'DRAFT'\n | 'ORDERED'\n | 'PARTIALLY_RECEIVED'\n | 'RECEIVED'\n | 'CANCELLED';\n\nexport type CustomerKind = 'B2C' | 'B2B';\nexport type CustomerRole = 'CUSTOMER' | 'SUPPLIER';\n\nexport class MiaStock {\n static async publishLowStockAlert(event: LowStockEvent): Promise<void> {\n await MessagingBus.publish('mia.menu.stock.low', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface PurchaseOrderRecordedEvent {\n poId: string;\n brandId: string;\n branchId?: string;\n supplierCustomerId?: string;\n poNumber?: string;\n // Cash basis (inc-VAT) total in SAR; consumers use this directly as amount.\n amountIncVat: number;\n // Convenience extra in case a consumer wants the pre-tax figure.\n amountExVat?: number;\n occurredAt: string; // ISO string\n // Actor that ordered the PO. `staffId` set when ordered by staff, otherwise\n // `userId` is set (e.g. brand owner). Consumers map to their own actor shape.\n staffId?: string;\n userId?: string;\n}\n\nexport interface PurchaseOrderCancelledEvent {\n poId: string;\n brandId: string;\n}\n\nexport class MiaExpenses {\n static async publishPurchaseOrderRecorded(event: PurchaseOrderRecordedEvent): Promise<void> {\n await MessagingBus.publish('mia.expenses.purchase-order.recorded', event);\n }\n\n static async publishPurchaseOrderCancelled(event: PurchaseOrderCancelledEvent): Promise<void> {\n await MessagingBus.publish('mia.expenses.purchase-order.cancelled', event);\n }\n}\n"],"mappings":"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","names":["e"]}
|
package/dist/index.mjs
CHANGED
|
@@ -71,8 +71,9 @@ MiaServerResponse.sendError = (error, status = 400) => {
|
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
// src/messaging-bus.ts
|
|
74
|
-
import { connect, JSONCodec } from "nats";
|
|
75
|
-
var
|
|
74
|
+
import { connect, JSONCodec, StorageType, AckPolicy } from "nats";
|
|
75
|
+
var SEVEN_DAYS_NS = 7 * 24 * 60 * 60 * 1e9;
|
|
76
|
+
var MessagingBus = class _MessagingBus {
|
|
76
77
|
static subscribe(channel, queue, onMessage) {
|
|
77
78
|
return __async(this, null, function* () {
|
|
78
79
|
if (!process.env.NATS_HOST) {
|
|
@@ -114,6 +115,113 @@ var MessagingBus = class {
|
|
|
114
115
|
yield nc.drain();
|
|
115
116
|
});
|
|
116
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Idempotently ensure a JetStream stream exists for `streamSubjects`.
|
|
120
|
+
* Tolerates create races between concurrent callers.
|
|
121
|
+
*/
|
|
122
|
+
static ensureStream(jsm, streamName, streamSubjects) {
|
|
123
|
+
return __async(this, null, function* () {
|
|
124
|
+
try {
|
|
125
|
+
yield jsm.streams.info(streamName);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
try {
|
|
128
|
+
yield jsm.streams.add({
|
|
129
|
+
name: streamName,
|
|
130
|
+
subjects: streamSubjects,
|
|
131
|
+
storage: StorageType.File,
|
|
132
|
+
max_age: SEVEN_DAYS_NS
|
|
133
|
+
});
|
|
134
|
+
} catch (e2) {
|
|
135
|
+
yield jsm.streams.info(streamName);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Durable publish: persists the message to a JetStream stream and resolves
|
|
142
|
+
* ONLY after the server acknowledges the store. Unlike `publish` (fire-and-
|
|
143
|
+
* forget core NATS), a resolved `publishDurable` means the message survives
|
|
144
|
+
* even when no subscriber is currently connected — use it for alerts/events
|
|
145
|
+
* that must never be lost while a consumer is down.
|
|
146
|
+
*/
|
|
147
|
+
static publishDurable(_0, _1, _2) {
|
|
148
|
+
return __async(this, arguments, function* (channel, message, streamName, streamSubjects = [channel]) {
|
|
149
|
+
if (!process.env.NATS_HOST) {
|
|
150
|
+
throw new Error("NATS_HOST ENV is not defined");
|
|
151
|
+
}
|
|
152
|
+
const nc = yield connect({ servers: process.env.NATS_HOST });
|
|
153
|
+
try {
|
|
154
|
+
const jsm = yield nc.jetstreamManager();
|
|
155
|
+
yield _MessagingBus.ensureStream(jsm, streamName, streamSubjects);
|
|
156
|
+
const jc = JSONCodec();
|
|
157
|
+
yield nc.jetstream().publish(channel, jc.encode(message));
|
|
158
|
+
} finally {
|
|
159
|
+
yield nc.drain();
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Durable subscribe: binds a durable JetStream consumer so messages
|
|
165
|
+
* published while this service was down are REPLAYED when it reconnects
|
|
166
|
+
* (unlike `subscribe`, which only delivers live messages). Each message is
|
|
167
|
+
* acked after the handler resolves; a handler that throws naks for redelivery
|
|
168
|
+
* (bounded by maxDeliver so a poison message can't loop forever).
|
|
169
|
+
*/
|
|
170
|
+
static subscribeDurable(_0, _1, _2, _3) {
|
|
171
|
+
return __async(this, arguments, function* (channel, streamName, durableName, onMessage, options = {}) {
|
|
172
|
+
var _a, _b;
|
|
173
|
+
if (!process.env.NATS_HOST) {
|
|
174
|
+
throw new Error("NATS_HOST ENV is not defined");
|
|
175
|
+
}
|
|
176
|
+
const streamSubjects = (_a = options.streamSubjects) != null ? _a : [channel];
|
|
177
|
+
const maxDeliver = (_b = options.maxDeliver) != null ? _b : 10;
|
|
178
|
+
const nc = yield connect({ servers: process.env.NATS_HOST });
|
|
179
|
+
const jsm = yield nc.jetstreamManager();
|
|
180
|
+
yield _MessagingBus.ensureStream(jsm, streamName, streamSubjects);
|
|
181
|
+
try {
|
|
182
|
+
yield jsm.consumers.info(streamName, durableName);
|
|
183
|
+
} catch (e) {
|
|
184
|
+
try {
|
|
185
|
+
yield jsm.consumers.add(streamName, {
|
|
186
|
+
durable_name: durableName,
|
|
187
|
+
ack_policy: AckPolicy.Explicit,
|
|
188
|
+
max_deliver: maxDeliver,
|
|
189
|
+
ack_wait: 60 * 1e9
|
|
190
|
+
// 60s
|
|
191
|
+
});
|
|
192
|
+
} catch (e2) {
|
|
193
|
+
yield jsm.consumers.info(streamName, durableName);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const consumer = yield nc.jetstream().consumers.get(streamName, durableName);
|
|
197
|
+
const messages = yield consumer.consume({ max_messages: 1 });
|
|
198
|
+
const sc = JSONCodec();
|
|
199
|
+
(() => __async(this, null, function* () {
|
|
200
|
+
try {
|
|
201
|
+
for (var iter = __forAwait(messages), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
|
|
202
|
+
const m = temp.value;
|
|
203
|
+
try {
|
|
204
|
+
yield onMessage(sc.decode(m.data));
|
|
205
|
+
m.ack();
|
|
206
|
+
} catch (err) {
|
|
207
|
+
console.error(`subscribeDurable(${channel}) handler error \u2014 will redeliver:`, err);
|
|
208
|
+
m.nak();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch (temp) {
|
|
212
|
+
error = [temp];
|
|
213
|
+
} finally {
|
|
214
|
+
try {
|
|
215
|
+
more && (temp = iter.return) && (yield temp.call(iter));
|
|
216
|
+
} finally {
|
|
217
|
+
if (error)
|
|
218
|
+
throw error[0];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}))().catch((err) => console.error(`subscribeDurable(${channel}) consume loop ended:`, err));
|
|
222
|
+
console.log(`durably listening for ${channel} via ${streamName}/${durableName}`);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
117
225
|
};
|
|
118
226
|
|
|
119
227
|
// src/notifications.ts
|
|
@@ -270,10 +378,24 @@ var MiaShifts = class {
|
|
|
270
378
|
};
|
|
271
379
|
|
|
272
380
|
// src/critical-errors.ts
|
|
381
|
+
var CRITICAL_ERRORS_STREAM = "CRITICAL_ERRORS";
|
|
382
|
+
var CRITICAL_ERRORS_SUBJECT = "mia.errors.critical";
|
|
273
383
|
var MiaCriticalErrors = class {
|
|
384
|
+
/**
|
|
385
|
+
* Publish a critical-error alert durably. Resolves only once the alert is
|
|
386
|
+
* persisted to the CRITICAL_ERRORS JetStream stream, so a resolved call
|
|
387
|
+
* guarantees the alert survives even if the notifications service is down —
|
|
388
|
+
* it is replayed to that durable consumer on recovery. Callers may therefore
|
|
389
|
+
* treat a resolved publish as "the operator will be notified".
|
|
390
|
+
*/
|
|
274
391
|
static publish(event) {
|
|
275
392
|
return __async(this, null, function* () {
|
|
276
|
-
yield MessagingBus.
|
|
393
|
+
yield MessagingBus.publishDurable(
|
|
394
|
+
CRITICAL_ERRORS_SUBJECT,
|
|
395
|
+
event,
|
|
396
|
+
CRITICAL_ERRORS_STREAM,
|
|
397
|
+
["mia.errors.*"]
|
|
398
|
+
);
|
|
277
399
|
});
|
|
278
400
|
}
|
|
279
401
|
};
|
|
@@ -312,6 +434,8 @@ var MiaExpenses = class {
|
|
|
312
434
|
}
|
|
313
435
|
};
|
|
314
436
|
export {
|
|
437
|
+
CRITICAL_ERRORS_STREAM,
|
|
438
|
+
CRITICAL_ERRORS_SUBJECT,
|
|
315
439
|
MessagingBus,
|
|
316
440
|
MiaAuditTrace,
|
|
317
441
|
MiaCriticalErrors,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mia-server-response.ts","../src/messaging-bus.ts","../src/notifications.ts","../src/customers.ts","../src/multilanguage.ts","../src/order-attributes.ts","../src/orders.ts","../src/shifts.ts","../src/critical-errors.ts","../src/audit-trace.ts","../src/stock.ts","../src/expenses.ts"],"sourcesContent":["export class MiaServerResponse {\n status?: number;\n message?: string;\n data: any;\n\n static send = (status: number, message: string, data: any) => {\n const response: MiaServerResponse = {\n status: status,\n message: message,\n data: data\n };\n return response;\n }\n static sendData = (data: any, status: number = 200) => {\n const response: MiaServerResponse = {\n status: status,\n message: \"Success\",\n data: data\n };\n return response;\n }\n static sendError = (error: any, status: number = 400) => {\n const response: MiaServerResponse = {\n status: status,\n message: \"Failed\",\n data: error\n };\n return response;\n }\n}\n","import { connect, JSONCodec } from \"nats\";\n\nexport class MessagingBus {\n static async subscribe(channel: string, queue: string, onMessage: Function) {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n const sc = JSONCodec();\n const subscription = nc.subscribe(channel, { queue: queue });\n\n (async (sub) => {\n\n console.log(`listening for ${sub.getSubject()} requests...`);\n\n for await (const m of sub) {\n const msg = sc.decode(m.data);\n onMessage(msg);\n }\n\n console.log(`subscription ${sub.getSubject()} drained.`);\n })(subscription);\n }\n\n static async publish(channel: string, message: any) {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n const jc = JSONCodec();\n nc.publish(channel, jc.encode(message));\n await nc.drain();\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\nexport interface Notification {\n title?: string,\n body: string,\n badge?: number,\n payload?: any,\n target?: string\n}\nexport interface UserNotification extends Notification {\n userId: string\n}\nexport interface DeviceNotification extends Notification {\n deviceId: string\n}\nexport interface TopicNotification extends Notification {\n topic: string,\n}\nexport interface SmsNotification extends Notification {\n mobile: string,\n}\nexport interface StaffNotification extends Notification {\n staffId: string\n brandId: string\n}\n\nexport interface PrintLine {\n text: string,\n alignment: 'left' | 'center' | 'right',\n size: 'normal' | 'large' | 'small',\n weight: 'normal' | 'bold'\n}\n\nexport interface OperateOrderNotificationPayload {\n orderId: string,\n serveTo: string | null,\n items: Array<{\n name: string,\n quantity: number,\n price: number,\n description: string | null\n }>,\n notes: string | null,\n lines: PrintLine[]\n}\n\nexport interface OperateOrderNotification {\n brandId: string,\n branchId: string,\n title: string,\n body: string,\n order: OperateOrderNotificationPayload\n}\n\nexport class MiaNotifications {\n static async sendtoDevice(message: DeviceNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoUserDevices(message: UserNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoTopic(message: TopicNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoSms(message: SmsNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoStaff(message: StaffNotification) {\n const topic = MiaNotifications.staffTopicFormatter(message.brandId, message.staffId);\n await MiaNotifications.sendtoTopic({\n topic,\n title: message.title,\n body: message.body,\n badge: message.badge,\n payload: message.payload,\n target: message.target\n });\n }\n static async sendOperateOrderNotification(message: OperateOrderNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, {\n ...message,\n type: 'operateOrder'\n });\n }\n static topicFormatter = (topic: string) => \"/topics/\" + topic.replace(/\\//g, \"~\")\n static operateOrderTopicFormatter = (brandId: string, branchId: string) =>\n `~operate~orders~${brandId}~${branchId}`\n static staffTopicFormatter = (brandId: string, staffId: string) =>\n `/topics/~staff~${brandId}~${staffId}`\n}","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface CustomerMobileVerifiedMessage {\n brandId: string;\n customer: any;\n isVerified: boolean;\n}\n\nexport interface CustomerActionMessage {\n brandId: string;\n type: string;\n customer: any;\n payload: any;\n}\n\nexport interface CustomerAddTagMessage {\n brandId: string;\n customer: any;\n tag: string;\n remove: boolean;\n}\n\nexport interface CustomerUpdateNotesMessage {\n brandId: string;\n customer: any;\n newNotes: string;\n}\n\nexport class MicrogemsCustomer {\n static async mobileVerified(message: CustomerMobileVerifiedMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async action(message: CustomerActionMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async addTag(message: CustomerAddTagMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async updateNotes(message: CustomerUpdateNotesMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n}","/**\n * Multilanguage String Type\n *\n * Represents text content that can be displayed in multiple languages.\n * Used across services for internationalization support.\n */\n\nexport interface MultilanguageString {\n /**\n * English text (required)\n */\n en: string;\n\n /**\n * Arabic text (optional)\n */\n ar?: string;\n}\n\n/**\n * Helper function to get text in preferred language with fallback\n *\n * @param text - Multilanguage string object\n * @param language - Preferred language ('en' | 'ar')\n * @returns Text in preferred language, or English if not available\n */\nexport function getLocalizedText(\n text: MultilanguageString,\n language: 'en' | 'ar' = 'en'\n): string {\n if (language === 'ar' && text.ar) {\n return text.ar;\n }\n return text.en;\n}\n\n/**\n * Helper function to create a multilanguage string from a plain string\n *\n * @param enText - English text\n * @param arText - Optional Arabic text\n * @returns MultilanguageString object\n */\nexport function createMultilanguageString(\n enText: string,\n arText?: string\n): MultilanguageString {\n const result: MultilanguageString = { en: enText };\n if (arText) {\n result.ar = arText;\n }\n return result;\n}\n","/**\n * Order Item Attributes\n *\n * Shared types for order item customization attributes used across:\n * - menu API (for menu item attributes)\n * - schedule API (for appointment service attributes)\n * - transactions API (for order item attributes)\n */\n\n/**\n * Single attribute value option\n */\nexport interface AttributeValue {\n /**\n * Unique identifier for this value option\n */\n id: string;\n\n /**\n * Display name of the value\n */\n name: string;\n\n /**\n * Additional price for selecting this value (0 if no extra cost)\n */\n price: number;\n\n /**\n * Whether this value is available/in-stock\n */\n isAvailable?: boolean;\n}\n\n/**\n * Attribute definition (e.g., \"Size\", \"Color\", \"Extras\")\n */\nexport interface OrderAttribute {\n /**\n * Unique identifier for this attribute\n */\n id: string;\n\n /**\n * Attribute name (e.g., \"Size\", \"Toppings\")\n */\n name: string;\n\n /**\n * Available values for this attribute\n */\n values: AttributeValue[];\n\n /**\n * Whether customer must select a value for this attribute\n */\n required?: boolean;\n\n /**\n * Allow multiple values to be selected (e.g., multiple toppings)\n */\n allowMultiple?: boolean;\n\n /**\n * Minimum number of selections (if allowMultiple = true)\n */\n minSelections?: number;\n\n /**\n * Maximum number of selections (if allowMultiple = true)\n */\n maxSelections?: number;\n}\n\n/**\n * Selected attribute for an order item\n * (What the customer actually selected)\n */\nexport interface SelectedOrderAttribute {\n /**\n * Reference to the attribute definition\n */\n attributeId: string;\n\n /**\n * Attribute name (denormalized for display)\n */\n attributeName: string;\n\n /**\n * Selected value(s)\n */\n selectedValues: {\n /**\n * Value ID\n */\n id: string;\n\n /**\n * Value name (denormalized for display)\n */\n name: string;\n\n /**\n * Price of this selected value\n */\n price: number;\n }[];\n\n /**\n * Total price for this attribute (sum of all selected values)\n */\n totalPrice: number;\n}\n\n/**\n * Array of selected attributes for an order item\n * This is what gets stored in Appointment.orderItemAttributes or Order.items[].attributes\n */\nexport type OrderItemAttributes = SelectedOrderAttribute[];\n\n/**\n * Helper function to calculate total price from selected attributes\n *\n * @param attributes - Array of selected attributes\n * @returns Total additional price from all attributes\n */\nexport function calculateAttributesPrice(\n attributes: OrderItemAttributes\n): number {\n return attributes.reduce((total, attr) => total + attr.totalPrice, 0);\n}\n\n/**\n * Helper function to validate selected attributes against definitions\n *\n * @param selected - Selected attributes\n * @param definitions - Attribute definitions\n * @returns Validation result with any errors\n */\nexport function validateSelectedAttributes(\n selected: OrderItemAttributes,\n definitions: OrderAttribute[]\n): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check required attributes\n for (const def of definitions) {\n if (def.required) {\n const selectedAttr = selected.find((s) => s.attributeId === def.id);\n if (!selectedAttr || selectedAttr.selectedValues.length === 0) {\n errors.push(`Attribute \"${def.name}\" is required`);\n }\n }\n }\n\n // Check selection counts\n for (const selectedAttr of selected) {\n const def = definitions.find((d) => d.id === selectedAttr.attributeId);\n if (!def) {\n errors.push(`Unknown attribute: ${selectedAttr.attributeName}`);\n continue;\n }\n\n const count = selectedAttr.selectedValues.length;\n\n if (!def.allowMultiple && count > 1) {\n errors.push(`Attribute \"${def.name}\" allows only one selection`);\n }\n\n if (def.minSelections && count < def.minSelections) {\n errors.push(\n `Attribute \"${def.name}\" requires at least ${def.minSelections} selections`\n );\n }\n\n if (def.maxSelections && count > def.maxSelections) {\n errors.push(\n `Attribute \"${def.name}\" allows at most ${def.maxSelections} selections`\n );\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport type ServiceType = 'MENU' | 'APPOINTMENT' | 'CREDIT_BUNDLE';\n\n// ZATCA-compliant buyer identification schemes for BR-KSA-81\nexport type BuyerIdentificationScheme = 'TIN'|'CRN'|'MOM'|'MLS'|'700'|'SAG'|'NAT'|'GCC'|'IQA'|'PAS'|'OTH';\n\nexport interface OrderItemForInvoice {\n idInService: string;\n name: string;\n description?: string;\n price: number;\n quantity: number;\n vatRate?: number;\n invoicable?: boolean; // defaults to true; set to false for items already paid (e.g., package redemptions)\n orderAttributes?: { id?: string; name: string; price: number; quantity?: number }[];\n}\n\nexport interface PaymentInfo {\n type: string;\n amount: number;\n reference?: string;\n}\n\n// Fee information for ZATCA invoice charges\nexport interface OrderFeeInfo {\n feeId?: string;\n name: string;\n type: string;\n value: number;\n calculatedAmount: number; // VAT-inclusive amount\n}\n\nexport interface CustomerInfo {\n name?: string;\n phoneNumber?: string;\n countryCode?: string;\n email?: string;\n vatNumber?: string;\n crNumber?: string;\n streetName?: string;\n buildingNumber?: string;\n cityName?: string;\n postalZone?: string;\n district?: string;\n identificationNumber?: string;\n identificationScheme?: BuyerIdentificationScheme;\n // B2C (default when undefined) vs B2B. Drives ZATCA Standard vs Simplified\n // selection downstream in the invoice service.\n kind?: 'B2C' | 'B2B';\n}\n\nexport interface OrderCreatedEvent {\n orderId: string;\n brandId: string;\n branchId?: string;\n source: ServiceType;\n subtotal: number;\n vatAmount: number;\n vatPercentage?: number;\n total: number;\n items: OrderItemForInvoice[];\n payments: PaymentInfo[];\n customerInfo?: CustomerInfo;\n dailyOrderNumber?: number;\n yearlyOrderNumber?: number;\n createdAt: Date;\n additionalFees?: OrderFeeInfo[]; // Order-level fees (minimum order, service, delivery, etc.)\n}\n\nexport class MiaOrders {\n static async publishOrderCreated(event: OrderCreatedEvent): Promise<void> {\n await MessagingBus.publish('mia.orders.created', event);\n }\n}\n\nexport interface PreorderPaymentEvent {\n preorderId: string;\n sourceId: string;\n source: ServiceType;\n brandId: string;\n branchId?: string;\n payment: PaymentInfo;\n totalPaid: number;\n total: number;\n isFullyPaid: boolean;\n orderId?: string;\n}\n\nexport class MiaPreorders {\n static async publishPaymentAdded(event: PreorderPaymentEvent): Promise<void> {\n await MessagingBus.publish('mia.preorders.payment-added', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface ShiftClosedEvent {\n shiftId: string;\n brandId: string;\n branchId: string;\n staffId: string;\n openedAt: string;\n closedAt: string;\n initialCash: number;\n expectedCash: number;\n closingCash: number;\n paymentSummary: Array<{ type: string; total: number; count: number }>;\n refundSummary: Array<{ type: string; total: number; count: number }>;\n totalRefunds: number;\n closingNotes?: string;\n staffName?: string;\n}\n\nexport class MiaShifts {\n static async publishShiftClosed(event: ShiftClosedEvent): Promise<void> {\n await MessagingBus.publish('mia.shifts.closed', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface CriticalErrorEvent {\n brandId: string;\n branchId?: string;\n service: string;\n errorType: string;\n summary: string;\n details: string;\n resourceId?: string;\n resourceType?: string;\n occurredAt: string;\n}\n\nexport class MiaCriticalErrors {\n static async publish(event: CriticalErrorEvent): Promise<void> {\n await MessagingBus.publish('mia.errors.critical', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport type AuditRiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\n\nexport interface AuditTraceEvent {\n action: string;\n riskLevel: AuditRiskLevel;\n service: string;\n staffId?: string;\n staffRole?: string;\n brandId: string;\n branchId?: string;\n financialImpact?: {\n amount: number;\n currency: string;\n };\n objectRef?: {\n collection: string;\n id: string;\n snapshot?: Record<string, any>;\n };\n details?: string;\n metadata?: Record<string, any>;\n performedAt?: string;\n}\n\nexport class MiaAuditTrace {\n static async publish(event: AuditTraceEvent): Promise<void> {\n await MessagingBus.publish('mia.audit.trace', {\n ...event,\n performedAt: event.performedAt || new Date().toISOString(),\n });\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface LowStockEvent {\n brandId: string;\n branchId: string;\n rawMaterialId: string;\n materialName: string;\n currentQuantity: number;\n lowThreshold: number;\n unit: string;\n}\n\nexport type StockTransactionType =\n | 'ORDER_DEDUCTION'\n | 'MANUAL_ADJUSTMENT'\n | 'SUPPLIER_RECEIPT'\n | 'TRANSFER_IN'\n | 'TRANSFER_OUT'\n | 'PRODUCTION_INPUT'\n | 'PRODUCTION_OUTPUT';\n\nexport type RawMaterialKind = 'SIMPLE' | 'COMPOSITE';\n\nexport type PurchaseOrderStatus =\n | 'DRAFT'\n | 'ORDERED'\n | 'PARTIALLY_RECEIVED'\n | 'RECEIVED'\n | 'CANCELLED';\n\nexport type CustomerKind = 'B2C' | 'B2B';\nexport type CustomerRole = 'CUSTOMER' | 'SUPPLIER';\n\nexport class MiaStock {\n static async publishLowStockAlert(event: LowStockEvent): Promise<void> {\n await MessagingBus.publish('mia.menu.stock.low', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface PurchaseOrderRecordedEvent {\n poId: string;\n brandId: string;\n branchId?: string;\n supplierCustomerId?: string;\n poNumber?: string;\n // Cash basis (inc-VAT) total in SAR; consumers use this directly as amount.\n amountIncVat: number;\n // Convenience extra in case a consumer wants the pre-tax figure.\n amountExVat?: number;\n occurredAt: string; // ISO string\n // Actor that ordered the PO. `staffId` set when ordered by staff, otherwise\n // `userId` is set (e.g. brand owner). Consumers map to their own actor shape.\n staffId?: string;\n userId?: string;\n}\n\nexport interface PurchaseOrderCancelledEvent {\n poId: string;\n brandId: string;\n}\n\nexport class MiaExpenses {\n static async publishPurchaseOrderRecorded(event: PurchaseOrderRecordedEvent): Promise<void> {\n await MessagingBus.publish('mia.expenses.purchase-order.recorded', event);\n }\n\n static async publishPurchaseOrderCancelled(event: PurchaseOrderCancelledEvent): Promise<void> {\n await MessagingBus.publish('mia.expenses.purchase-order.cancelled', event);\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/mia-server-response.ts","../src/messaging-bus.ts","../src/notifications.ts","../src/customers.ts","../src/multilanguage.ts","../src/order-attributes.ts","../src/orders.ts","../src/shifts.ts","../src/critical-errors.ts","../src/audit-trace.ts","../src/stock.ts","../src/expenses.ts"],"sourcesContent":["export class MiaServerResponse {\n status?: number;\n message?: string;\n data: any;\n\n static send = (status: number, message: string, data: any) => {\n const response: MiaServerResponse = {\n status: status,\n message: message,\n data: data\n };\n return response;\n }\n static sendData = (data: any, status: number = 200) => {\n const response: MiaServerResponse = {\n status: status,\n message: \"Success\",\n data: data\n };\n return response;\n }\n static sendError = (error: any, status: number = 400) => {\n const response: MiaServerResponse = {\n status: status,\n message: \"Failed\",\n data: error\n };\n return response;\n }\n}\n","import { connect, JSONCodec, StorageType, AckPolicy } from \"nats\";\n\nconst SEVEN_DAYS_NS = 7 * 24 * 60 * 60 * 1_000_000_000;\n\nexport class MessagingBus {\n static async subscribe(channel: string, queue: string, onMessage: Function) {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n const sc = JSONCodec();\n const subscription = nc.subscribe(channel, { queue: queue });\n\n (async (sub) => {\n\n console.log(`listening for ${sub.getSubject()} requests...`);\n\n for await (const m of sub) {\n const msg = sc.decode(m.data);\n onMessage(msg);\n }\n\n console.log(`subscription ${sub.getSubject()} drained.`);\n })(subscription);\n }\n\n static async publish(channel: string, message: any) {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n const jc = JSONCodec();\n nc.publish(channel, jc.encode(message));\n await nc.drain();\n }\n\n /**\n * Idempotently ensure a JetStream stream exists for `streamSubjects`.\n * Tolerates create races between concurrent callers.\n */\n private static async ensureStream(\n jsm: any,\n streamName: string,\n streamSubjects: string[]\n ): Promise<void> {\n try {\n await jsm.streams.info(streamName);\n } catch {\n try {\n await jsm.streams.add({\n name: streamName,\n subjects: streamSubjects,\n storage: StorageType.File,\n max_age: SEVEN_DAYS_NS,\n });\n } catch {\n // Another instance may have created it between info() and add().\n await jsm.streams.info(streamName); // throws only if it truly doesn't exist\n }\n }\n }\n\n /**\n * Durable publish: persists the message to a JetStream stream and resolves\n * ONLY after the server acknowledges the store. Unlike `publish` (fire-and-\n * forget core NATS), a resolved `publishDurable` means the message survives\n * even when no subscriber is currently connected — use it for alerts/events\n * that must never be lost while a consumer is down.\n */\n static async publishDurable(\n channel: string,\n message: any,\n streamName: string,\n streamSubjects: string[] = [channel]\n ): Promise<void> {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const nc = await connect({ servers: process.env.NATS_HOST });\n try {\n const jsm = await nc.jetstreamManager();\n await MessagingBus.ensureStream(jsm, streamName, streamSubjects);\n const jc = JSONCodec();\n // js.publish awaits the PubAck — it rejects if the store fails.\n await nc.jetstream().publish(channel, jc.encode(message));\n } finally {\n await nc.drain();\n }\n }\n\n /**\n * Durable subscribe: binds a durable JetStream consumer so messages\n * published while this service was down are REPLAYED when it reconnects\n * (unlike `subscribe`, which only delivers live messages). Each message is\n * acked after the handler resolves; a handler that throws naks for redelivery\n * (bounded by maxDeliver so a poison message can't loop forever).\n */\n static async subscribeDurable(\n channel: string,\n streamName: string,\n durableName: string,\n onMessage: (message: any) => Promise<void> | void,\n options: { streamSubjects?: string[]; maxDeliver?: number } = {}\n ): Promise<void> {\n if (!process.env.NATS_HOST) {\n throw new Error(\"NATS_HOST ENV is not defined\");\n }\n const streamSubjects = options.streamSubjects ?? [channel];\n const maxDeliver = options.maxDeliver ?? 10;\n\n const nc = await connect({ servers: process.env.NATS_HOST });\n const jsm = await nc.jetstreamManager();\n await MessagingBus.ensureStream(jsm, streamName, streamSubjects);\n\n try {\n await jsm.consumers.info(streamName, durableName);\n } catch {\n try {\n await jsm.consumers.add(streamName, {\n durable_name: durableName,\n ack_policy: AckPolicy.Explicit,\n max_deliver: maxDeliver,\n ack_wait: 60 * 1_000_000_000, // 60s\n });\n } catch {\n await jsm.consumers.info(streamName, durableName); // lost create race; ok if it now exists\n }\n }\n\n const consumer = await nc.jetstream().consumers.get(streamName, durableName);\n const messages = await consumer.consume({ max_messages: 1 });\n const sc = JSONCodec();\n (async () => {\n for await (const m of messages) {\n try {\n await onMessage(sc.decode(m.data));\n m.ack();\n } catch (err) {\n console.error(`subscribeDurable(${channel}) handler error — will redeliver:`, err);\n m.nak();\n }\n }\n })().catch((err) => console.error(`subscribeDurable(${channel}) consume loop ended:`, err));\n\n console.log(`durably listening for ${channel} via ${streamName}/${durableName}`);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\nexport interface Notification {\n title?: string,\n body: string,\n badge?: number,\n payload?: any,\n target?: string\n}\nexport interface UserNotification extends Notification {\n userId: string\n}\nexport interface DeviceNotification extends Notification {\n deviceId: string\n}\nexport interface TopicNotification extends Notification {\n topic: string,\n}\nexport interface SmsNotification extends Notification {\n mobile: string,\n}\nexport interface StaffNotification extends Notification {\n staffId: string\n brandId: string\n}\n\nexport interface PrintLine {\n text: string,\n alignment: 'left' | 'center' | 'right',\n size: 'normal' | 'large' | 'small',\n weight: 'normal' | 'bold'\n}\n\nexport interface OperateOrderNotificationPayload {\n orderId: string,\n serveTo: string | null,\n items: Array<{\n name: string,\n quantity: number,\n price: number,\n description: string | null\n }>,\n notes: string | null,\n lines: PrintLine[]\n}\n\nexport interface OperateOrderNotification {\n brandId: string,\n branchId: string,\n title: string,\n body: string,\n order: OperateOrderNotificationPayload\n}\n\nexport class MiaNotifications {\n static async sendtoDevice(message: DeviceNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoUserDevices(message: UserNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoTopic(message: TopicNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoSms(message: SmsNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, message);\n }\n static async sendtoStaff(message: StaffNotification) {\n const topic = MiaNotifications.staffTopicFormatter(message.brandId, message.staffId);\n await MiaNotifications.sendtoTopic({\n topic,\n title: message.title,\n body: message.body,\n badge: message.badge,\n payload: message.payload,\n target: message.target\n });\n }\n static async sendOperateOrderNotification(message: OperateOrderNotification) {\n await MessagingBus.publish(`mia.notifications.${process.env.HOSTNAME || \"general\"}`, {\n ...message,\n type: 'operateOrder'\n });\n }\n static topicFormatter = (topic: string) => \"/topics/\" + topic.replace(/\\//g, \"~\")\n static operateOrderTopicFormatter = (brandId: string, branchId: string) =>\n `~operate~orders~${brandId}~${branchId}`\n static staffTopicFormatter = (brandId: string, staffId: string) =>\n `/topics/~staff~${brandId}~${staffId}`\n}","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface CustomerMobileVerifiedMessage {\n brandId: string;\n customer: any;\n isVerified: boolean;\n}\n\nexport interface CustomerActionMessage {\n brandId: string;\n type: string;\n customer: any;\n payload: any;\n}\n\nexport interface CustomerAddTagMessage {\n brandId: string;\n customer: any;\n tag: string;\n remove: boolean;\n}\n\nexport interface CustomerUpdateNotesMessage {\n brandId: string;\n customer: any;\n newNotes: string;\n}\n\nexport class MicrogemsCustomer {\n static async mobileVerified(message: CustomerMobileVerifiedMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async action(message: CustomerActionMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async addTag(message: CustomerAddTagMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n\n static async updateNotes(message: CustomerUpdateNotesMessage) {\n await MessagingBus.publish(`mia.customers.${process.env.HOSTNAME || \"general\"}`, message);\n }\n}","/**\n * Multilanguage String Type\n *\n * Represents text content that can be displayed in multiple languages.\n * Used across services for internationalization support.\n */\n\nexport interface MultilanguageString {\n /**\n * English text (required)\n */\n en: string;\n\n /**\n * Arabic text (optional)\n */\n ar?: string;\n}\n\n/**\n * Helper function to get text in preferred language with fallback\n *\n * @param text - Multilanguage string object\n * @param language - Preferred language ('en' | 'ar')\n * @returns Text in preferred language, or English if not available\n */\nexport function getLocalizedText(\n text: MultilanguageString,\n language: 'en' | 'ar' = 'en'\n): string {\n if (language === 'ar' && text.ar) {\n return text.ar;\n }\n return text.en;\n}\n\n/**\n * Helper function to create a multilanguage string from a plain string\n *\n * @param enText - English text\n * @param arText - Optional Arabic text\n * @returns MultilanguageString object\n */\nexport function createMultilanguageString(\n enText: string,\n arText?: string\n): MultilanguageString {\n const result: MultilanguageString = { en: enText };\n if (arText) {\n result.ar = arText;\n }\n return result;\n}\n","/**\n * Order Item Attributes\n *\n * Shared types for order item customization attributes used across:\n * - menu API (for menu item attributes)\n * - schedule API (for appointment service attributes)\n * - transactions API (for order item attributes)\n */\n\n/**\n * Single attribute value option\n */\nexport interface AttributeValue {\n /**\n * Unique identifier for this value option\n */\n id: string;\n\n /**\n * Display name of the value\n */\n name: string;\n\n /**\n * Additional price for selecting this value (0 if no extra cost)\n */\n price: number;\n\n /**\n * Whether this value is available/in-stock\n */\n isAvailable?: boolean;\n}\n\n/**\n * Attribute definition (e.g., \"Size\", \"Color\", \"Extras\")\n */\nexport interface OrderAttribute {\n /**\n * Unique identifier for this attribute\n */\n id: string;\n\n /**\n * Attribute name (e.g., \"Size\", \"Toppings\")\n */\n name: string;\n\n /**\n * Available values for this attribute\n */\n values: AttributeValue[];\n\n /**\n * Whether customer must select a value for this attribute\n */\n required?: boolean;\n\n /**\n * Allow multiple values to be selected (e.g., multiple toppings)\n */\n allowMultiple?: boolean;\n\n /**\n * Minimum number of selections (if allowMultiple = true)\n */\n minSelections?: number;\n\n /**\n * Maximum number of selections (if allowMultiple = true)\n */\n maxSelections?: number;\n}\n\n/**\n * Selected attribute for an order item\n * (What the customer actually selected)\n */\nexport interface SelectedOrderAttribute {\n /**\n * Reference to the attribute definition\n */\n attributeId: string;\n\n /**\n * Attribute name (denormalized for display)\n */\n attributeName: string;\n\n /**\n * Selected value(s)\n */\n selectedValues: {\n /**\n * Value ID\n */\n id: string;\n\n /**\n * Value name (denormalized for display)\n */\n name: string;\n\n /**\n * Price of this selected value\n */\n price: number;\n }[];\n\n /**\n * Total price for this attribute (sum of all selected values)\n */\n totalPrice: number;\n}\n\n/**\n * Array of selected attributes for an order item\n * This is what gets stored in Appointment.orderItemAttributes or Order.items[].attributes\n */\nexport type OrderItemAttributes = SelectedOrderAttribute[];\n\n/**\n * Helper function to calculate total price from selected attributes\n *\n * @param attributes - Array of selected attributes\n * @returns Total additional price from all attributes\n */\nexport function calculateAttributesPrice(\n attributes: OrderItemAttributes\n): number {\n return attributes.reduce((total, attr) => total + attr.totalPrice, 0);\n}\n\n/**\n * Helper function to validate selected attributes against definitions\n *\n * @param selected - Selected attributes\n * @param definitions - Attribute definitions\n * @returns Validation result with any errors\n */\nexport function validateSelectedAttributes(\n selected: OrderItemAttributes,\n definitions: OrderAttribute[]\n): { valid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check required attributes\n for (const def of definitions) {\n if (def.required) {\n const selectedAttr = selected.find((s) => s.attributeId === def.id);\n if (!selectedAttr || selectedAttr.selectedValues.length === 0) {\n errors.push(`Attribute \"${def.name}\" is required`);\n }\n }\n }\n\n // Check selection counts\n for (const selectedAttr of selected) {\n const def = definitions.find((d) => d.id === selectedAttr.attributeId);\n if (!def) {\n errors.push(`Unknown attribute: ${selectedAttr.attributeName}`);\n continue;\n }\n\n const count = selectedAttr.selectedValues.length;\n\n if (!def.allowMultiple && count > 1) {\n errors.push(`Attribute \"${def.name}\" allows only one selection`);\n }\n\n if (def.minSelections && count < def.minSelections) {\n errors.push(\n `Attribute \"${def.name}\" requires at least ${def.minSelections} selections`\n );\n }\n\n if (def.maxSelections && count > def.maxSelections) {\n errors.push(\n `Attribute \"${def.name}\" allows at most ${def.maxSelections} selections`\n );\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport type ServiceType = 'MENU' | 'APPOINTMENT' | 'CREDIT_BUNDLE';\n\n// ZATCA-compliant buyer identification schemes for BR-KSA-81\nexport type BuyerIdentificationScheme = 'TIN'|'CRN'|'MOM'|'MLS'|'700'|'SAG'|'NAT'|'GCC'|'IQA'|'PAS'|'OTH';\n\nexport interface OrderItemForInvoice {\n idInService: string;\n name: string;\n description?: string;\n price: number;\n quantity: number;\n vatRate?: number;\n invoicable?: boolean; // defaults to true; set to false for items already paid (e.g., package redemptions)\n orderAttributes?: { id?: string; name: string; price: number; quantity?: number }[];\n}\n\nexport interface PaymentInfo {\n type: string;\n amount: number;\n reference?: string;\n}\n\n// Fee information for ZATCA invoice charges\nexport interface OrderFeeInfo {\n feeId?: string;\n name: string;\n type: string;\n value: number;\n calculatedAmount: number; // VAT-inclusive amount\n}\n\nexport interface CustomerInfo {\n name?: string;\n phoneNumber?: string;\n countryCode?: string;\n email?: string;\n vatNumber?: string;\n crNumber?: string;\n streetName?: string;\n buildingNumber?: string;\n cityName?: string;\n postalZone?: string;\n district?: string;\n identificationNumber?: string;\n identificationScheme?: BuyerIdentificationScheme;\n // B2C (default when undefined) vs B2B. Drives ZATCA Standard vs Simplified\n // selection downstream in the invoice service.\n kind?: 'B2C' | 'B2B';\n}\n\nexport interface OrderCreatedEvent {\n orderId: string;\n brandId: string;\n branchId?: string;\n source: ServiceType;\n subtotal: number;\n vatAmount: number;\n vatPercentage?: number;\n total: number;\n items: OrderItemForInvoice[];\n payments: PaymentInfo[];\n customerInfo?: CustomerInfo;\n dailyOrderNumber?: number;\n yearlyOrderNumber?: number;\n createdAt: Date;\n additionalFees?: OrderFeeInfo[]; // Order-level fees (minimum order, service, delivery, etc.)\n}\n\nexport class MiaOrders {\n static async publishOrderCreated(event: OrderCreatedEvent): Promise<void> {\n await MessagingBus.publish('mia.orders.created', event);\n }\n}\n\nexport interface PreorderPaymentEvent {\n preorderId: string;\n sourceId: string;\n source: ServiceType;\n brandId: string;\n branchId?: string;\n payment: PaymentInfo;\n totalPaid: number;\n total: number;\n isFullyPaid: boolean;\n orderId?: string;\n}\n\nexport class MiaPreorders {\n static async publishPaymentAdded(event: PreorderPaymentEvent): Promise<void> {\n await MessagingBus.publish('mia.preorders.payment-added', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface ShiftClosedEvent {\n shiftId: string;\n brandId: string;\n branchId: string;\n staffId: string;\n openedAt: string;\n closedAt: string;\n initialCash: number;\n expectedCash: number;\n closingCash: number;\n paymentSummary: Array<{ type: string; total: number; count: number }>;\n refundSummary: Array<{ type: string; total: number; count: number }>;\n totalRefunds: number;\n closingNotes?: string;\n staffName?: string;\n}\n\nexport class MiaShifts {\n static async publishShiftClosed(event: ShiftClosedEvent): Promise<void> {\n await MessagingBus.publish('mia.shifts.closed', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface CriticalErrorEvent {\n brandId: string;\n branchId?: string;\n service: string;\n errorType: string;\n summary: string;\n details: string;\n resourceId?: string;\n resourceType?: string;\n occurredAt: string;\n}\n\nexport const CRITICAL_ERRORS_STREAM = 'CRITICAL_ERRORS';\nexport const CRITICAL_ERRORS_SUBJECT = 'mia.errors.critical';\n\nexport class MiaCriticalErrors {\n /**\n * Publish a critical-error alert durably. Resolves only once the alert is\n * persisted to the CRITICAL_ERRORS JetStream stream, so a resolved call\n * guarantees the alert survives even if the notifications service is down —\n * it is replayed to that durable consumer on recovery. Callers may therefore\n * treat a resolved publish as \"the operator will be notified\".\n */\n static async publish(event: CriticalErrorEvent): Promise<void> {\n await MessagingBus.publishDurable(\n CRITICAL_ERRORS_SUBJECT,\n event,\n CRITICAL_ERRORS_STREAM,\n ['mia.errors.*']\n );\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport type AuditRiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\n\nexport interface AuditTraceEvent {\n action: string;\n riskLevel: AuditRiskLevel;\n service: string;\n staffId?: string;\n staffRole?: string;\n brandId: string;\n branchId?: string;\n financialImpact?: {\n amount: number;\n currency: string;\n };\n objectRef?: {\n collection: string;\n id: string;\n snapshot?: Record<string, any>;\n };\n details?: string;\n metadata?: Record<string, any>;\n performedAt?: string;\n}\n\nexport class MiaAuditTrace {\n static async publish(event: AuditTraceEvent): Promise<void> {\n await MessagingBus.publish('mia.audit.trace', {\n ...event,\n performedAt: event.performedAt || new Date().toISOString(),\n });\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface LowStockEvent {\n brandId: string;\n branchId: string;\n rawMaterialId: string;\n materialName: string;\n currentQuantity: number;\n lowThreshold: number;\n unit: string;\n}\n\nexport type StockTransactionType =\n | 'ORDER_DEDUCTION'\n | 'MANUAL_ADJUSTMENT'\n | 'SUPPLIER_RECEIPT'\n | 'TRANSFER_IN'\n | 'TRANSFER_OUT'\n | 'PRODUCTION_INPUT'\n | 'PRODUCTION_OUTPUT';\n\nexport type RawMaterialKind = 'SIMPLE' | 'COMPOSITE';\n\nexport type PurchaseOrderStatus =\n | 'DRAFT'\n | 'ORDERED'\n | 'PARTIALLY_RECEIVED'\n | 'RECEIVED'\n | 'CANCELLED';\n\nexport type CustomerKind = 'B2C' | 'B2B';\nexport type CustomerRole = 'CUSTOMER' | 'SUPPLIER';\n\nexport class MiaStock {\n static async publishLowStockAlert(event: LowStockEvent): Promise<void> {\n await MessagingBus.publish('mia.menu.stock.low', event);\n }\n}\n","import { MessagingBus } from \"./messaging-bus\";\n\nexport interface PurchaseOrderRecordedEvent {\n poId: string;\n brandId: string;\n branchId?: string;\n supplierCustomerId?: string;\n poNumber?: string;\n // Cash basis (inc-VAT) total in SAR; consumers use this directly as amount.\n amountIncVat: number;\n // Convenience extra in case a consumer wants the pre-tax figure.\n amountExVat?: number;\n occurredAt: string; // ISO string\n // Actor that ordered the PO. `staffId` set when ordered by staff, otherwise\n // `userId` is set (e.g. brand owner). Consumers map to their own actor shape.\n staffId?: string;\n userId?: string;\n}\n\nexport interface PurchaseOrderCancelledEvent {\n poId: string;\n brandId: string;\n}\n\nexport class MiaExpenses {\n static async publishPurchaseOrderRecorded(event: PurchaseOrderRecordedEvent): Promise<void> {\n await MessagingBus.publish('mia.expenses.purchase-order.recorded', event);\n }\n\n static async publishPurchaseOrderCancelled(event: PurchaseOrderCancelledEvent): Promise<void> {\n await MessagingBus.publish('mia.expenses.purchase-order.cancelled', event);\n }\n}\n"],"mappings":"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","names":["e"]}
|