@litusarchieve18/agricore-utils 1.0.1

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.js ADDED
@@ -0,0 +1,894 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ACT: () => ACT,
24
+ AccessLevel: () => AccessLevel,
25
+ AgriLogger: () => AgriLogger,
26
+ AppError: () => AppError,
27
+ ApprovalStatus: () => ApprovalStatus,
28
+ ApprovalType: () => ApprovalType,
29
+ AuditStatus: () => AuditStatus,
30
+ AuditType: () => AuditType,
31
+ CAT: () => CAT,
32
+ Converter: () => Converter,
33
+ CropCycleStatus: () => CropCycleStatus,
34
+ ERROR_DICTIONARY: () => ERROR_DICTIONARY,
35
+ EmitterType: () => EmitterType,
36
+ EnabledModule: () => EnabledModule,
37
+ ErrorFactory: () => ErrorFactory,
38
+ FORM_REGISTRY: () => FORM_REGISTRY,
39
+ FacilityType: () => FacilityType,
40
+ HarvestMethod: () => HarvestMethod,
41
+ LinkedStatus: () => LinkedStatus,
42
+ MOD: () => MOD,
43
+ MODULE_LABELS: () => MODULE_LABELS,
44
+ MicroclimateZone: () => MicroclimateZone,
45
+ MimeType: () => MimeType,
46
+ NettingType: () => NettingType,
47
+ NotificationMode: () => NotificationMode,
48
+ OrderStatus: () => OrderStatus,
49
+ PhysicalState: () => PhysicalState,
50
+ PlantingMethod: () => PlantingMethod,
51
+ RESOURCE_PATHS: () => RESOURCE_PATHS,
52
+ RelationshipType: () => RelationshipType,
53
+ RowOrientation: () => RowOrientation,
54
+ SENTINEL_UUID: () => SENTINEL_UUID,
55
+ SoilTexture: () => SoilTexture,
56
+ TASK_TYPE_REGISTRY: () => TASK_TYPE_REGISTRY,
57
+ TargetEntityType: () => TargetEntityType,
58
+ TaskStatus: () => TaskStatus,
59
+ TaskType: () => TaskType,
60
+ TransactionType: () => TransactionType,
61
+ TrellisType: () => TrellisType,
62
+ UserPrivilege: () => UserPrivilege,
63
+ UserRole: () => UserRole,
64
+ Validator: () => Validator,
65
+ VigorRating: () => VigorRating,
66
+ WaterSource: () => WaterSource,
67
+ assertCanWrite: () => assertCanWrite,
68
+ buildRlsFilter: () => buildRlsFilter,
69
+ canRead: () => canRead,
70
+ canWrite: () => canWrite,
71
+ extractAppCode: () => extractAppCode,
72
+ generateCanonicalId: () => generateCanonicalId,
73
+ getAcceptedMimeTypes: () => getAcceptedMimeTypes,
74
+ getApproveAction: () => getApproveAction,
75
+ getFormMeta: () => getFormMeta,
76
+ getFormsGroupedByModule: () => getFormsGroupedByModule,
77
+ getTaskTypeOptions: () => getTaskTypeOptions,
78
+ handleAppErrorResponse: () => handleAppErrorResponse,
79
+ isTabVisible: () => isTabVisible,
80
+ isTabWritable: () => isTabWritable,
81
+ resolveAccess: () => resolveAccess,
82
+ resolveAppError: () => resolveAppError,
83
+ resolveAuthScopeId: () => resolveAuthScopeId,
84
+ resolveError: () => resolveError,
85
+ resolveMenuVisibility: () => resolveMenuVisibility,
86
+ withAgriLogging: () => withAgriLogging,
87
+ withSaveMiddleware: () => withSaveMiddleware
88
+ });
89
+ module.exports = __toCommonJS(index_exports);
90
+
91
+ // src/logger.ts
92
+ var SENSITIVE_KEYS = ["password", "token", "secret", "apiKey", "creditCard", "authorization"];
93
+ function redact(obj) {
94
+ if (!obj || typeof obj !== "object") return obj;
95
+ if (Array.isArray(obj)) return obj.map(redact);
96
+ const redactedObj = { ...obj };
97
+ for (const key in redactedObj) {
98
+ if (SENSITIVE_KEYS.includes(key.toLowerCase())) {
99
+ redactedObj[key] = "[REDACTED]";
100
+ } else if (typeof redactedObj[key] === "object") {
101
+ redactedObj[key] = redact(redactedObj[key]);
102
+ }
103
+ }
104
+ return redactedObj;
105
+ }
106
+ var AgriLogger = {
107
+ log(level, message, meta) {
108
+ const logEntry = {
109
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
110
+ level,
111
+ message,
112
+ functionName: meta.functionName,
113
+ duration: meta.duration ? `${meta.duration}ms` : void 0,
114
+ user: meta.session ? {
115
+ id: meta.session.userId,
116
+ companyId: meta.session.companyId
117
+ } : "SYSTEM",
118
+ context: {
119
+ input: redact(meta.input),
120
+ output: redact(meta.output),
121
+ error: meta.error instanceof Error ? { message: meta.error.message, stack: meta.error.stack } : redact(meta.error)
122
+ }
123
+ };
124
+ console.log(JSON.stringify(logEntry));
125
+ }
126
+ };
127
+ function withAgriLogging(fn, functionName, session) {
128
+ return (async (...args) => {
129
+ const startTime = Date.now();
130
+ AgriLogger.log("INFO", `Started ${functionName}`, { functionName, session, input: args });
131
+ try {
132
+ const result = await fn(...args);
133
+ const duration = Date.now() - startTime;
134
+ AgriLogger.log("INFO", `Completed ${functionName}`, {
135
+ functionName,
136
+ session,
137
+ output: result,
138
+ duration
139
+ });
140
+ return result;
141
+ } catch (error) {
142
+ const duration = Date.now() - startTime;
143
+ AgriLogger.log("ERROR", `Failed ${functionName}`, {
144
+ functionName,
145
+ session,
146
+ error,
147
+ duration
148
+ });
149
+ throw error;
150
+ }
151
+ });
152
+ }
153
+
154
+ // src/validators.ts
155
+ var Validator = {
156
+ /** * 1. Email: The classic. Uses a standard RFC 5322 regex.
157
+ */
158
+ isEmail: (val) => {
159
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
160
+ return emailRegex.test(val);
161
+ },
162
+ /** * 2. UUID v4: Critical for your canonicalId and authScopeId checks.
163
+ */
164
+ isUUID: (value) => {
165
+ if (!value) return false;
166
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
167
+ return uuidRegex.test(value);
168
+ },
169
+ /** * 3. ISO Date String: Ensures dates are stored in UTC/ISO format.
170
+ */
171
+ isIsoDate: (val) => {
172
+ if (!val) return false;
173
+ const d = new Date(val);
174
+ return !isNaN(d.getTime()) && val.includes("T") && val.endsWith("Z");
175
+ },
176
+ isPositiveNumber: (value) => {
177
+ return value != null && typeof value === "number" && value > 0;
178
+ },
179
+ /** * 4. Strong Password: Min 8 chars, 1 uppercase, 1 lowercase, 1 number, 1 special char.
180
+ */
181
+ isStrongPassword: (val) => {
182
+ const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
183
+ return passwordRegex.test(val);
184
+ },
185
+ /** * 5. Phone Number (E.164): The international standard (e.g., +61400123456).
186
+ */
187
+ isE164Phone: (val) => {
188
+ const phoneRegex = /^\+[1-9]\d{1,14}$/;
189
+ return phoneRegex.test(val);
190
+ },
191
+ /** * 6. Range Checker: Useful for temperature sensors or quantity limits.
192
+ */
193
+ isInRange: (val, min, max) => {
194
+ return val >= min && val <= max;
195
+ },
196
+ /** * 7. URL: Basic validation for external documentation links.
197
+ */
198
+ isValidUrl: (val) => {
199
+ try {
200
+ new URL(val);
201
+ return true;
202
+ } catch {
203
+ return false;
204
+ }
205
+ },
206
+ /** * 8. Slug: Useful for URL-friendly names (e.g. "sunset-farm-01").
207
+ */
208
+ isSlug: (val) => {
209
+ const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
210
+ return slugRegex.test(val);
211
+ },
212
+ isNotEmpty: (value) => {
213
+ return value != null && value.trim().length > 0;
214
+ },
215
+ /** * 9. Is Non-Empty Array: Ensures you don't save an empty list of IDs.
216
+ */
217
+ hasItems: (val) => {
218
+ return Array.isArray(val) && val.length > 0;
219
+ },
220
+ /** * 10. Coordinates: Validate GPS Latitude/Longitude for Farm maps.
221
+ */
222
+ isValidCoordinate: (lat, lng) => {
223
+ return lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
224
+ }
225
+ };
226
+
227
+ // src/converters.ts
228
+ var CONSTANTS = {
229
+ HA_TO_ACRE: 2.47105,
230
+ LITER_TO_GAL_US: 0.264172,
231
+ LITER_TO_GAL_UK: 0.219969
232
+ };
233
+ function roundTo(value, decimals = 2) {
234
+ const factor = Math.pow(10, decimals);
235
+ return Math.round(value * factor) / factor;
236
+ }
237
+ var Converter = {
238
+ // ─── Area Conversions ──────────────────────────────────────────────────────
239
+ /**
240
+ * Hectares to Acres
241
+ * Formula: $Acres = Hectares \times 2.47105$
242
+ */
243
+ haToAcre: (ha, precision = 2) => {
244
+ return roundTo(ha * CONSTANTS.HA_TO_ACRE, precision);
245
+ },
246
+ /**
247
+ * Acres to Hectares
248
+ * Formula: $Hectares = Acres \div 2.47105$
249
+ */
250
+ acreToHa: (acre, precision = 2) => {
251
+ return roundTo(acre / CONSTANTS.HA_TO_ACRE, precision);
252
+ },
253
+ // ─── Volume Conversions ────────────────────────────────────────────────────
254
+ /**
255
+ * Liters to US Gallons
256
+ * Formula: $Gallons = Liters \times 0.264172$
257
+ */
258
+ literToGal: (liters, precision = 2) => {
259
+ return roundTo(liters * CONSTANTS.LITER_TO_GAL_US, precision);
260
+ },
261
+ /**
262
+ * US Gallons to Liters
263
+ * Formula: $Liters = Gallons \div 0.264172$
264
+ */
265
+ galToLiter: (gal, precision = 2) => {
266
+ return roundTo(gal / CONSTANTS.LITER_TO_GAL_US, precision);
267
+ },
268
+ // ─── Mass Conversions (Bonus) ──────────────────────────────────────────────
269
+ /**
270
+ * Kilograms to Pounds (useful for yield/harvest data)
271
+ */
272
+ kgToLbs: (kg, precision = 2) => {
273
+ return roundTo(kg * 2.20462, precision);
274
+ }
275
+ };
276
+
277
+ // src/index.ts
278
+ var MOD = {
279
+ OPERATIONS: "OPR",
280
+ EQUIPMENT: "EQP",
281
+ PACKHOUSE: "PCK",
282
+ FARM: "FRM",
283
+ SAFETY: "SAF",
284
+ TEAM_HR: "THR",
285
+ RECORDS: "REC",
286
+ ADMIN: "ADM",
287
+ DOCUMENTS: "DOC",
288
+ ALERTS: "ALT",
289
+ INFRA: "INF",
290
+ SESSION: "SES"
291
+ // ← added: used in generateSessionContext but was missing from errorRegistry.js
292
+ };
293
+ var CAT = {
294
+ SUBSCRIPTION: "1",
295
+ PERMISSION: "2",
296
+ VALIDATION: "3",
297
+ INTEGRITY: "4"
298
+ };
299
+ var ACT = {
300
+ GENERIC: "00",
301
+ VIEW: "01",
302
+ CREATE: "02",
303
+ EDIT: "03",
304
+ DELETE: "04",
305
+ UPLOAD: "05"
306
+ };
307
+ var SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
308
+ var AppError = class extends Error {
309
+ status;
310
+ appCode;
311
+ isOperational;
312
+ constructor(status, mod, cat, act, message) {
313
+ super(message);
314
+ this.status = status;
315
+ this.appCode = `${mod}-${status}-${cat}${act}`;
316
+ this.isOperational = true;
317
+ Object.setPrototypeOf(this, new.target.prototype);
318
+ }
319
+ };
320
+ var ErrorFactory = {
321
+ unauthorized: (mod, msg = "Authentication required") => new AppError(401, mod, CAT.PERMISSION, ACT.GENERIC, msg),
322
+ forbidden: (mod, act, msg = "Access denied") => new AppError(403, mod, CAT.PERMISSION, act, msg),
323
+ badRequest: (mod, act, msg = "Bad request") => new AppError(400, mod, CAT.VALIDATION, act, msg),
324
+ notFound: (mod, act = ACT.GENERIC, msg = "Record not found") => new AppError(404, mod, CAT.VALIDATION, act, msg),
325
+ conflict: (mod, act, msg = "Record already exists") => new AppError(409, mod, CAT.INTEGRITY, act, msg),
326
+ integrity: (mod, act = ACT.GENERIC, msg = "Data integrity violation") => new AppError(409, mod, CAT.INTEGRITY, act, msg),
327
+ subscription: (mod, msg = "Module locked or subscription required") => new AppError(403, mod, CAT.SUBSCRIPTION, ACT.GENERIC, msg),
328
+ internal: (mod, msg = "Internal server error") => new AppError(500, mod, CAT.INTEGRITY, ACT.GENERIC, msg)
329
+ };
330
+ var handleAppErrorResponse = (error, mod = MOD.ADMIN) => {
331
+ if (error instanceof AppError && error.isOperational) {
332
+ return Response.json(
333
+ { error: { appCode: error.appCode, message: error.message } },
334
+ { status: error.status }
335
+ );
336
+ }
337
+ const e = error;
338
+ console.error(`[${mod}] UNEXPECTED ERROR:`, e?.message);
339
+ console.error(`[${mod}] STACK:`, e?.stack);
340
+ return Response.json(
341
+ { error: { appCode: `${mod}-500-000`, message: "An unexpected server error occurred. Please try again." } },
342
+ { status: 500 }
343
+ );
344
+ };
345
+ var MODULE_LABELS = {
346
+ [MOD.OPERATIONS]: "Operations",
347
+ [MOD.EQUIPMENT]: "Equipment & Fleet",
348
+ [MOD.PACKHOUSE]: "Packhouse",
349
+ [MOD.FARM]: "Farm Management",
350
+ [MOD.SAFETY]: "Safety & Compliance",
351
+ [MOD.TEAM_HR]: "Team & HR",
352
+ [MOD.RECORDS]: "Records & Reports",
353
+ [MOD.ADMIN]: "Admin Settings",
354
+ [MOD.DOCUMENTS]: "Documents",
355
+ [MOD.ALERTS]: "Alerts",
356
+ [MOD.INFRA]: "Infrastructure",
357
+ [MOD.SESSION]: "Session"
358
+ };
359
+ var ERROR_DICTIONARY = {
360
+ // ── Gate 1: Subscription ──────────────────────────────────────────────────
361
+ "100": { title: "Module Locked", text: "This feature is part of a module not enabled for your company. Contact your administrator to unlock it." },
362
+ // ── Gate 2: Permission ────────────────────────────────────────────────────
363
+ "200": { title: "Access Denied", text: "You do not have permission to perform this action. Contact your manager." },
364
+ "201": { title: "Restricted View", text: "Viewing this section requires higher access. Contact your administrator." },
365
+ "202": { title: "Creation Restricted", text: "You are not permitted to create new records here." },
366
+ "203": { title: "Edit Restricted", text: "You do not have permission to edit this record." },
367
+ "204": { title: "Protected Record", text: "You do not have permission to delete this record. Contact your administrator." },
368
+ "205": { title: "Upload Restricted", text: "You are not permitted to upload files here." },
369
+ // ── Gate 3: Validation ────────────────────────────────────────────────────
370
+ "300": { title: "Invalid Input", text: "Something is wrong with the data provided. Please review and try again." },
371
+ "301": { title: "Cannot Load Data", text: "Required information is missing to load this page." },
372
+ "302": { title: "Missing Fields", text: "Please fill in all required fields before saving." },
373
+ "303": { title: "Invalid Update", text: "The data you are trying to save is not valid. Please check your inputs." },
374
+ "304": { title: "Cannot Delete", text: "The record cannot be deleted due to a validation issue." },
375
+ "305": { title: "Upload Failed", text: "The file could not be uploaded. Please check the file type and size." },
376
+ // ── Gate 4: Integrity ─────────────────────────────────────────────────────
377
+ "400": { title: "Data Conflict", text: "A conflict occurred with an existing record. Please review and try again." },
378
+ "402": { title: "Duplicate Entry", text: "A record with this name or identifier already exists." },
379
+ "403": { title: "Edit Conflict", text: "This record was recently updated by someone else. Please refresh and try again." },
380
+ "404": { title: "Record In Use", text: "This record cannot be deleted because it is referenced by other data." }
381
+ };
382
+ var FALLBACK = {
383
+ title: "Something Went Wrong",
384
+ text: "An unexpected error occurred. Please try again or contact your administrator."
385
+ };
386
+ var resolveAppError = (appCode) => {
387
+ if (!appCode || typeof appCode !== "string") {
388
+ return { ...FALLBACK, module: "UNK", moduleLabel: "Unknown", status: 500, catAct: "000", isSubscriptionIssue: false, isPermissionIssue: false };
389
+ }
390
+ const parts = appCode.split("-");
391
+ if (parts.length !== 3) {
392
+ return { ...FALLBACK, module: "UNK", moduleLabel: "Unknown", status: 500, catAct: "000", isSubscriptionIssue: false, isPermissionIssue: false };
393
+ }
394
+ const [mod, status, catAct] = parts;
395
+ const details = ERROR_DICTIONARY[catAct] || FALLBACK;
396
+ return {
397
+ title: details.title,
398
+ text: details.text,
399
+ module: mod,
400
+ moduleLabel: MODULE_LABELS[mod] || mod,
401
+ status: parseInt(status, 10),
402
+ catAct,
403
+ isSubscriptionIssue: catAct.startsWith("1"),
404
+ isPermissionIssue: catAct.startsWith("2")
405
+ };
406
+ };
407
+ var extractAppCode = (error) => {
408
+ const e = error;
409
+ return e?.response?.data?.error?.appCode ?? e?.data?.error?.appCode ?? null;
410
+ };
411
+ var resolveError = (error) => {
412
+ const appCode = extractAppCode(error);
413
+ if (appCode) return resolveAppError(appCode);
414
+ const e = error;
415
+ return { ...FALLBACK, module: "UNK", moduleLabel: "Unknown", status: e?.status || 500, catAct: "000", isSubscriptionIssue: false, isPermissionIssue: false };
416
+ };
417
+ function generateCanonicalId() {
418
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
419
+ return crypto.randomUUID();
420
+ }
421
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
422
+ const r = Math.random() * 16 | 0;
423
+ return (c === "x" ? r : r & 3 | 8).toString(16);
424
+ });
425
+ }
426
+ function resolveAuthScopeId(fields = {}) {
427
+ return fields.facilityId || fields.legalEntityId || fields.companyId || SENTINEL_UUID;
428
+ }
429
+ function withSaveMiddleware(data) {
430
+ const canonicalId = data.canonicalId || generateCanonicalId();
431
+ return {
432
+ ...data,
433
+ canonicalId,
434
+ authScopeId: data.authScopeId || resolveAuthScopeId(data)
435
+ };
436
+ }
437
+ function buildRlsFilter(session, extra = {}) {
438
+ return { ...extra, authScopeId: { $in: session.readScopes || [] } };
439
+ }
440
+ function canRead(session, authScopeId) {
441
+ return session.readScopes?.includes(authScopeId) ?? false;
442
+ }
443
+ function canWrite(session, authScopeId, resourcePath) {
444
+ const override = session.resourceOverrides?.[authScopeId]?.[resourcePath];
445
+ if (override === "WRITE") return true;
446
+ if (override === "NONE" || override === "READ") return false;
447
+ return session.writeScopes?.includes(authScopeId) ?? false;
448
+ }
449
+ function assertCanWrite(session, authScopeId, resourcePath) {
450
+ const override = session.resourceOverrides?.[authScopeId]?.[resourcePath];
451
+ if (override === "WRITE") return;
452
+ if (override === "NONE" || override === "READ") {
453
+ throw new AppError(
454
+ 403,
455
+ MOD.SESSION,
456
+ CAT.PERMISSION,
457
+ ACT.GENERIC,
458
+ `You do not have write access to "${resourcePath}" in this scope.`
459
+ );
460
+ }
461
+ if (session.writeScopes?.includes(authScopeId)) return;
462
+ throw new AppError(
463
+ 403,
464
+ MOD.SESSION,
465
+ CAT.PERMISSION,
466
+ ACT.GENERIC,
467
+ "You do not have permission to modify records in this scope."
468
+ );
469
+ }
470
+ function resolveAccess(session, activeScopeId, tabConfig) {
471
+ const resourcePath = tabConfig?.resourceId;
472
+ const overrides = session.resourceOverrides?.[activeScopeId] ?? {};
473
+ if (resourcePath) {
474
+ const segments = resourcePath.split(".");
475
+ for (let len = segments.length; len >= 1; len--) {
476
+ const path = segments.slice(0, len).join(".");
477
+ const override = overrides[path];
478
+ if (override === "NONE" || override === "READ" || override === "WRITE") return override;
479
+ }
480
+ }
481
+ const allowedRoles = tabConfig?.allowedRoles ?? [];
482
+ const requiredPrivileges = tabConfig?.requiredPrivileges ?? [];
483
+ const hasRestrictions = allowedRoles.length > 0 || requiredPrivileges.length > 0;
484
+ if (hasRestrictions) {
485
+ const role = session?.role ?? "";
486
+ const privileges = session?.privileges ?? [];
487
+ const passesRole = allowedRoles.length > 0 && allowedRoles.includes(role);
488
+ const passesPrivilege = requiredPrivileges.length > 0 && requiredPrivileges.some((p) => privileges.includes(p));
489
+ return passesRole || passesPrivilege ? "WRITE" : "NONE";
490
+ }
491
+ return "READ";
492
+ }
493
+ function resolveMenuVisibility(session, authScopeId, resourcePath) {
494
+ const override = session.resourceOverrides?.[authScopeId]?.[resourcePath];
495
+ if (override === "NONE") return "hidden";
496
+ if (override === "READ") return "read_only";
497
+ if (override === "WRITE") return "active";
498
+ if (session.writeScopes?.includes(authScopeId)) return "active";
499
+ if (session.readScopes?.includes(authScopeId)) return "read_only";
500
+ return "hidden";
501
+ }
502
+ function isTabVisible(session, activeScopeId, tabConfig) {
503
+ return resolveAccess(session, activeScopeId, tabConfig) !== "NONE";
504
+ }
505
+ function isTabWritable(session, activeScopeId, tabConfig) {
506
+ return resolveAccess(session, activeScopeId, tabConfig) === "WRITE";
507
+ }
508
+ var UserRole = {
509
+ OWNER: "owner",
510
+ MANAGER: "manager",
511
+ SUPERVISOR: "supervisor",
512
+ LEADING_HAND: "leadingHand",
513
+ WORKER: "worker"
514
+ };
515
+ var UserPrivilege = {
516
+ FINANCE: "finance",
517
+ HR: "hr",
518
+ MECHANIC: "mechanic",
519
+ AUDIT_COMPLIANCE: "auditCompliance",
520
+ ADMIN: "admin"
521
+ };
522
+ var AccessLevel = {
523
+ NONE: "NONE",
524
+ READ: "READ",
525
+ WRITE: "WRITE"
526
+ };
527
+ var EnabledModule = {
528
+ FARM_MANAGEMENT: "farm_management",
529
+ PACKHOUSE: "packhouse",
530
+ ACCOMMODATION: "accommodation"
531
+ };
532
+ var FacilityType = {
533
+ FARM: "farm",
534
+ PACKHOUSE: "packhouse",
535
+ ACCOMMODATION: "accommodation"
536
+ };
537
+ var LinkedStatus = {
538
+ NOT_LINKED: "not_linked",
539
+ PENDING_LINK: "pending_link",
540
+ LINKED: "linked"
541
+ };
542
+ var RelationshipType = {
543
+ SUPPLIER: "supplier",
544
+ CUSTOMER: "customer",
545
+ BOTH: "both"
546
+ };
547
+ var ApprovalType = {
548
+ ROLE: "role",
549
+ PRIVILEGE: "privilege",
550
+ SPECIFIC_USERS: "specific_users"
551
+ };
552
+ var TransactionType = {
553
+ PURCHASE_ORDER: "purchase_order",
554
+ SALES_ORDER: "sales_order",
555
+ REPAIR: "repair",
556
+ FUEL: "fuel",
557
+ OTHER: "other"
558
+ };
559
+ var ApprovalStatus = {
560
+ PENDING: "pending",
561
+ APPROVED: "approved",
562
+ REJECTED: "rejected"
563
+ };
564
+ var OrderStatus = {
565
+ DRAFT: "draft",
566
+ PENDING_FINANCE: "pending_finance",
567
+ NEW: "new",
568
+ ACKNOWLEDGED: "acknowledged",
569
+ IN_PROGRESS: "in_progress",
570
+ SHIPPED: "shipped",
571
+ RECEIVED: "received",
572
+ CANCELLED: "cancelled"
573
+ };
574
+ var NotificationMode = {
575
+ INTERNAL: "internal",
576
+ EXTERNAL_EMAIL: "external_email",
577
+ NONE: "none"
578
+ };
579
+ var TaskType = {
580
+ KML_PARSE: "KML_PARSE",
581
+ INVOICE_OCR: "INVOICE_OCR",
582
+ AUDIT_UPLOAD: "AUDIT_UPLOAD",
583
+ REPORT_GEN: "REPORT_GEN"
584
+ };
585
+ var TaskStatus = {
586
+ PENDING: "PENDING",
587
+ PROCESSING: "PROCESSING",
588
+ COMPLETED: "COMPLETED",
589
+ FAILED: "FAILED",
590
+ AWAITING_REVIEW: "AWAITING_REVIEW",
591
+ VERIFIED: "VERIFIED",
592
+ REJECTED: "REJECTED"
593
+ };
594
+ var MimeType = {
595
+ KML: "application/vnd.google-earth.kml+xml",
596
+ KMZ: "application/vnd.google-earth.kmz",
597
+ PDF: "application/pdf",
598
+ XLSX: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
599
+ XLS: "application/vnd.ms-excel",
600
+ JPEG: "image/jpeg",
601
+ PNG: "image/png"
602
+ };
603
+ var TargetEntityType = {
604
+ FARM: "FARM",
605
+ ZONE: "ZONE",
606
+ BLOCK: "BLOCK",
607
+ FUEL_SITE: "FUEL_SITE",
608
+ WORKSHOP: "WORKSHOP",
609
+ AUTO: "AUTO"
610
+ };
611
+ var AuditType = {
612
+ FRESHCARE: "Freshcare",
613
+ HACCP: "HACCP",
614
+ WHS: "WHS",
615
+ CUSTOM: "Custom"
616
+ };
617
+ var AuditStatus = {
618
+ DRAFT: "DRAFT",
619
+ IN_REVIEW: "IN_REVIEW",
620
+ FINALIZED: "FINALIZED"
621
+ };
622
+ var SoilTexture = {
623
+ SAND: "SAND",
624
+ LOAMY_SAND: "LOAMY_SAND",
625
+ SANDY_LOAM: "SANDY_LOAM",
626
+ LOAM: "LOAM",
627
+ SILT_LOAM: "SILT_LOAM",
628
+ SILT: "SILT",
629
+ SANDY_CLAY_LOAM: "SANDY_CLAY_LOAM",
630
+ CLAY_LOAM: "CLAY_LOAM",
631
+ SILTY_CLAY_LOAM: "SILTY_CLAY_LOAM",
632
+ SANDY_CLAY: "SANDY_CLAY",
633
+ SILTY_CLAY: "SILTY_CLAY",
634
+ CLAY: "CLAY"
635
+ };
636
+ var MicroclimateZone = {
637
+ STANDARD: "STANDARD",
638
+ FROST_POCKET: "FROST_POCKET",
639
+ WIND_CORRIDOR: "WIND_CORRIDOR",
640
+ EXPOSED_RIDGE: "EXPOSED_RIDGE",
641
+ HEAT_TRAP: "HEAT_TRAP",
642
+ RIPARIAN: "RIPARIAN"
643
+ };
644
+ var PlantingMethod = {
645
+ SQUARE: "SQUARE",
646
+ RECTANGULAR: "RECTANGULAR",
647
+ HEXAGONAL: "HEXAGONAL",
648
+ QUINCUNX: "QUINCUNX",
649
+ CONTOUR: "CONTOUR"
650
+ };
651
+ var RowOrientation = {
652
+ NORTH_SOUTH: "NORTH_SOUTH",
653
+ EAST_WEST: "EAST_WEST",
654
+ NORTHEAST_SOUTHWEST: "NORTHEAST_SOUTHWEST",
655
+ NORTHWEST_SOUTHEAST: "NORTHWEST_SOUTHEAST"
656
+ };
657
+ var EmitterType = {
658
+ SURFACE_DRIP: "SURFACE_DRIP",
659
+ SUBSURFACE_DRIP: "SUBSURFACE_DRIP",
660
+ MICRO_SPRINKLER: "MICRO_SPRINKLER",
661
+ JET_SPRAY: "JET_SPRAY",
662
+ INLINE_DRIPPER: "INLINE_DRIPPER",
663
+ PRESSURE_COMPENSATED: "PRESSURE_COMPENSATED"
664
+ };
665
+ var TrellisType = {
666
+ NONE: "NONE",
667
+ VERTICAL_AXIS: "VERTICAL_AXIS",
668
+ TWO_D_PLANE: "2D_PLANE",
669
+ V_TRELLIS: "V_TRELLIS",
670
+ T_BAR: "T_BAR",
671
+ PERGOLA: "PERGOLA",
672
+ Y_TRELLIS: "Y_TRELLIS"
673
+ };
674
+ var NettingType = {
675
+ NONE: "NONE",
676
+ OVERHEAD_HAIL: "OVERHEAD_HAIL",
677
+ SIDE_BIRD: "SIDE_BIRD",
678
+ FULL_EXCLUSION: "FULL_EXCLUSION",
679
+ SHADE_30: "SHADE_30",
680
+ SHADE_50: "SHADE_50",
681
+ CROSS_OVER: "CROSS_OVER"
682
+ };
683
+ var HarvestMethod = {
684
+ HAND: "HAND",
685
+ MACHINE: "MACHINE"
686
+ };
687
+ var VigorRating = {
688
+ VERY_LOW: "VERY_LOW",
689
+ LOW: "LOW",
690
+ MODERATE: "MODERATE",
691
+ HIGH: "HIGH",
692
+ VERY_HIGH: "VERY_HIGH"
693
+ };
694
+ var CropCycleStatus = {
695
+ NEW: "NEW",
696
+ CONFIRMED: "CONFIRMED",
697
+ ACTIVE: "ACTIVE",
698
+ COMPLETED: "COMPLETED"
699
+ };
700
+ var WaterSource = {
701
+ BORE_WELL: "BORE_WELL",
702
+ RIVER_STREAM: "RIVER_STREAM",
703
+ ON_FARM_DAM: "ON_FARM_DAM",
704
+ IRRIGATION_SCHEME: "IRRIGATION_SCHEME",
705
+ MUNICIPAL: "MUNICIPAL",
706
+ RECLAIMED_WASTE: "RECLAIMED_WASTE",
707
+ RAINWATER: "RAINWATER"
708
+ };
709
+ var PhysicalState = {
710
+ LIQUID: "LIQUID",
711
+ GAS: "GAS"
712
+ };
713
+ var FORM_REGISTRY = {
714
+ // ── Farm Management ──────────────────────────────────────────────────────
715
+ farmForm: { label: "Farm Profile", menuUrl: "/admin", module: "Farm Management" },
716
+ zoneForm: { label: "Zone Mapping", menuUrl: "/admin", module: "Farm Management" },
717
+ blockForm: { label: "Block Definition", menuUrl: "/admin", module: "Farm Management" },
718
+ // ── Crop & Variety ───────────────────────────────────────────────────────
719
+ cropForm: { label: "Crop Profile", menuUrl: "/admin", module: "Crop Library" },
720
+ varietyForm: { label: "Variety Blueprint", menuUrl: "/admin", module: "Crop Library" },
721
+ // ── Administration ───────────────────────────────────────────────────────
722
+ userAccessForm: { label: "User Access & Roles", menuUrl: "/admin/users", module: "Administration" },
723
+ companySettingsForm: { label: "Company Settings", menuUrl: "/admin/company", module: "Administration" },
724
+ financeTierForm: { label: "Finance Approval Tier", menuUrl: "/admin/company", module: "Administration" },
725
+ // ── Audit & Compliance ───────────────────────────────────────────────────
726
+ auditProjectForm: { label: "Audit Project", menuUrl: "/admin", module: "Audit & Compliance" }
727
+ };
728
+ function getFormMeta(componentId) {
729
+ return FORM_REGISTRY[componentId] ?? null;
730
+ }
731
+ function getFormsGroupedByModule() {
732
+ const groups = {};
733
+ for (const [id, meta] of Object.entries(FORM_REGISTRY)) {
734
+ if (!groups[meta.module]) groups[meta.module] = [];
735
+ groups[meta.module].push({ id, ...meta });
736
+ }
737
+ return groups;
738
+ }
739
+ var TASK_TYPE_REGISTRY = {
740
+ INVOICE_OCR: {
741
+ label: "Invoice / Receipt OCR",
742
+ targetEntity: "FuelInvoice",
743
+ description: "Extract invoice data (supplier, amount, date, line items) using AI.",
744
+ mimeTypes: [MimeType.PDF, MimeType.JPEG, MimeType.PNG],
745
+ icon: "FileText",
746
+ approveAction: "CREATE_FUEL_INVOICE"
747
+ },
748
+ KML_PARSE: {
749
+ label: "KML / Farm Boundary",
750
+ targetEntity: "Zone",
751
+ description: "Parse KML file to extract GPS polygon boundaries for a farm zone.",
752
+ mimeTypes: [MimeType.KML],
753
+ icon: "MapPin",
754
+ approveAction: "UPDATE_ZONE_POLYGON"
755
+ },
756
+ AUDIT_UPLOAD: {
757
+ label: "Audit / Compliance Document",
758
+ targetEntity: "AuditManifest",
759
+ description: "Attach evidence documents to a compliance audit project.",
760
+ mimeTypes: [MimeType.PDF, MimeType.JPEG, MimeType.PNG, MimeType.XLSX],
761
+ icon: "ShieldCheck",
762
+ approveAction: "LINK_AUDIT_MANIFEST"
763
+ },
764
+ REPORT_GEN: {
765
+ label: "Report Generation",
766
+ targetEntity: "AuditProject",
767
+ description: "Upload a completed audit report for storage and linking.",
768
+ mimeTypes: [MimeType.PDF],
769
+ icon: "BarChart2",
770
+ approveAction: "ATTACH_REPORT_FILE"
771
+ }
772
+ };
773
+ var getTaskTypeOptions = () => Object.entries(TASK_TYPE_REGISTRY).map(([value, config]) => ({
774
+ value,
775
+ label: config.label,
776
+ description: config.description,
777
+ mimeTypes: config.mimeTypes,
778
+ icon: config.icon
779
+ }));
780
+ var getAcceptedMimeTypes = (taskType) => {
781
+ const config = TASK_TYPE_REGISTRY[taskType];
782
+ return config ? config.mimeTypes.join(",") : "*/*";
783
+ };
784
+ var getApproveAction = (taskType) => {
785
+ return TASK_TYPE_REGISTRY[taskType]?.approveAction ?? null;
786
+ };
787
+ var RESOURCE_PATHS = {
788
+ // ── Top-level menu sections ────────────────────────────────────────────────
789
+ dashboard: "dashboard",
790
+ operations: "operations",
791
+ equipment: "equipment",
792
+ safety: "safety",
793
+ team: "team",
794
+ records: "records",
795
+ adminSettings: "adminSettings",
796
+ // ── Records & Reports ──────────────────────────────────────────────────────
797
+ documentHub: "records.documentHub",
798
+ documentHubTemplates: "records.documentHub.templates",
799
+ documentHubFolder: "records.documentHub.companyFolder",
800
+ documentHubExtraction: "records.documentHub.extractionHub",
801
+ documentHubQueue: "records.documentHub.verificationQueue",
802
+ analytics: "records.analytics",
803
+ // ── Admin Settings ─────────────────────────────────────────────────────────
804
+ adminUserRoster: "adminSettings.userRoster",
805
+ adminRolesPermissions: "adminSettings.rolesPermissions",
806
+ adminBlueprintsSites: "adminSettings.blueprintsSites",
807
+ adminCompanySettings: "adminSettings.companySettings",
808
+ // ── Operations ────────────────────────────────────────────────────────────
809
+ farmManagement: "operations.farmManagement",
810
+ packhouse: "operations.packhouse",
811
+ accommodation: "operations.accommodation",
812
+ // ── Equipment & Fleet ──────────────────────────────────────────────────────
813
+ equipPreStart: "equipment.preStart",
814
+ equipMaintenance: "equipment.maintenance",
815
+ equipFuelRegister: "equipment.fuelRegister",
816
+ equipCalibration: "equipment.calibration",
817
+ equipFleet: "equipment.fleetManagement",
818
+ // ── Safety, Quality & Compliance ──────────────────────────────────────────
819
+ safetyAuditing: "safety.auditing",
820
+ safetyFood: "safety.foodSafety",
821
+ safetyWhs: "safety.whs",
822
+ safetyCleaning: "safety.cleaning",
823
+ // ── Team & HR ──────────────────────────────────────────────────────────────
824
+ teamStaff: "team.staff",
825
+ teamTraining: "team.training"
826
+ };
827
+ // Annotate the CommonJS export names for ESM import in node:
828
+ 0 && (module.exports = {
829
+ ACT,
830
+ AccessLevel,
831
+ AgriLogger,
832
+ AppError,
833
+ ApprovalStatus,
834
+ ApprovalType,
835
+ AuditStatus,
836
+ AuditType,
837
+ CAT,
838
+ Converter,
839
+ CropCycleStatus,
840
+ ERROR_DICTIONARY,
841
+ EmitterType,
842
+ EnabledModule,
843
+ ErrorFactory,
844
+ FORM_REGISTRY,
845
+ FacilityType,
846
+ HarvestMethod,
847
+ LinkedStatus,
848
+ MOD,
849
+ MODULE_LABELS,
850
+ MicroclimateZone,
851
+ MimeType,
852
+ NettingType,
853
+ NotificationMode,
854
+ OrderStatus,
855
+ PhysicalState,
856
+ PlantingMethod,
857
+ RESOURCE_PATHS,
858
+ RelationshipType,
859
+ RowOrientation,
860
+ SENTINEL_UUID,
861
+ SoilTexture,
862
+ TASK_TYPE_REGISTRY,
863
+ TargetEntityType,
864
+ TaskStatus,
865
+ TaskType,
866
+ TransactionType,
867
+ TrellisType,
868
+ UserPrivilege,
869
+ UserRole,
870
+ Validator,
871
+ VigorRating,
872
+ WaterSource,
873
+ assertCanWrite,
874
+ buildRlsFilter,
875
+ canRead,
876
+ canWrite,
877
+ extractAppCode,
878
+ generateCanonicalId,
879
+ getAcceptedMimeTypes,
880
+ getApproveAction,
881
+ getFormMeta,
882
+ getFormsGroupedByModule,
883
+ getTaskTypeOptions,
884
+ handleAppErrorResponse,
885
+ isTabVisible,
886
+ isTabWritable,
887
+ resolveAccess,
888
+ resolveAppError,
889
+ resolveAuthScopeId,
890
+ resolveError,
891
+ resolveMenuVisibility,
892
+ withAgriLogging,
893
+ withSaveMiddleware
894
+ });