@litusarchieve18/agricore-utils 1.0.7 → 1.0.9
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/README.md +17 -1
- package/dist/index.d.mts +16 -7
- package/dist/index.d.ts +16 -7
- package/dist/index.js +124 -26
- package/dist/index.mjs +122 -26
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -90,6 +90,18 @@ const recordToSave = {
|
|
|
90
90
|
...infrastructure, // Injects canonicalId, companyId, legalEntityId, etc.
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
+
To completely eliminate database bottlenecks and solve serverless rate-limiting, AgriCore has migrated to a state-of-the-art, "Zero-DB" authorization architecture. By decoupling our security rules from the database SDK, our backend API endpoints now resolve complex multi-tenant boundaries and granular role-based access controls in roughly 1 millisecond.
|
|
94
|
+
|
|
95
|
+
Key Features of the New Security Engine:
|
|
96
|
+
|
|
97
|
+
One-Time Session Generation: On login, the backend dynamically aggregates a user's full hierarchical profile—including role assignments, availableScopes (Company > Legal Entity > Facility), and granular resourceOverrides—into a single, cryptographically signed JSON Web Token (JWT).
|
|
98
|
+
|
|
99
|
+
Zero-DB Library Validations: The @litusarchieve18/agricore-utils library has been upgraded to utilize pure, synchronous functions. Deep hierarchical access checks (findSpecificOverride) and tenant isolation (assertTenantBoundary) are now resolved directly against the JWT payload in-memory, completely bypassing the database for routine requests.
|
|
100
|
+
|
|
101
|
+
Smart Frontend Interceptor: A centralized React hook (useApiClient) acts as an automated API wrapper. It seamlessly injects the user's customJWT and the globally selected activeScopeId into every outbound request. This keeps UI forms completely decoupled from authentication logic.
|
|
102
|
+
|
|
103
|
+
Instant Context Switching: Because the JWT securely holds the user's entire "VIP List" of permitted scopes, users can instantly switch between farms, legal entities, and facilities in the frontend global dropdown without triggering a new authentication handshake or database query.
|
|
104
|
+
|
|
93
105
|
🛠️ Development & Publishing Guide
|
|
94
106
|
Zero Dependencies
|
|
95
107
|
To keep the package ultra-lightweight (~30KB), this library has zero runtime dependencies. All build tools (tsup, typescript) are strictly kept in devDependencies, and the package.json uses the "files": ["dist"] array to ensure only compiled code is uploaded.
|
|
@@ -117,7 +129,11 @@ Run the publish command:
|
|
|
117
129
|
Bash
|
|
118
130
|
npm publish --access public
|
|
119
131
|
📝 Version History
|
|
120
|
-
|
|
132
|
+
V1.0.9 — added jwt function
|
|
133
|
+
|
|
134
|
+
v1.0.8 — added temporary RESOURCE_REQUIREMENTS
|
|
135
|
+
|
|
136
|
+
v1.0.7 — add RESOURCE_MODULE_MAP
|
|
121
137
|
|
|
122
138
|
v1.0.6 — adding more logic
|
|
123
139
|
|
package/dist/index.d.mts
CHANGED
|
@@ -490,24 +490,33 @@ declare const RESOURCE_PATHS: {
|
|
|
490
490
|
declare const RESOURCE_MODULE_MAP: Record<string, string[]>;
|
|
491
491
|
declare const ACCESS_RANK: Record<AccessLevelType, number>;
|
|
492
492
|
declare const ROLE_SECTIONS_BY_KEY: Record<string, UserRoleType[]>;
|
|
493
|
+
interface ResourceRequirement {
|
|
494
|
+
allowedRoles?: UserRoleType[];
|
|
495
|
+
requiredPrivileges?: string[];
|
|
496
|
+
}
|
|
497
|
+
declare const EVERYONE: ("owner" | "manager" | "supervisor" | "leadingHand" | "worker")[];
|
|
498
|
+
declare const RESOURCE_REQUIREMENTS: Record<string, ResourceRequirement>;
|
|
493
499
|
declare function evaluateBaseAccess(resourcePath: string, session: {
|
|
494
500
|
role?: UserRoleType;
|
|
495
501
|
privileges?: string[];
|
|
496
502
|
enabledModules?: string[];
|
|
497
|
-
}, requirements?:
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
503
|
+
}, requirements?: ResourceRequirement): AccessLevelType;
|
|
504
|
+
/**
|
|
505
|
+
* The Judge: Overrides win over base access (Specificity principle).
|
|
506
|
+
*/
|
|
507
|
+
declare function resolveEffectiveAccess(base: AccessLevelType, override?: AccessLevelType | null): AccessLevelType;
|
|
508
|
+
declare function findSpecificOverride(session: AgriCoreSession, activeScopeId: string, resourcePath: string): AccessLevelType | null;
|
|
501
509
|
/**
|
|
502
510
|
* Tenant Boundary Guard (Backend Only)
|
|
503
511
|
* Note: db is typed as 'any' here because Base44 SDK types vary,
|
|
504
512
|
* but you can refine this with your internal SDK types.
|
|
505
513
|
*/
|
|
506
|
-
declare function assertTenantBoundary(db: any,
|
|
514
|
+
declare function assertTenantBoundary(db: any, session: AgriCoreSession, // Pass the JWT session instead of just requesterCompanyId
|
|
515
|
+
targets: {
|
|
507
516
|
targetUserId?: string;
|
|
508
517
|
authScopeId?: string;
|
|
509
518
|
expectedScopeType?: 'company' | 'legalEntity' | 'facility';
|
|
510
519
|
}): Promise<boolean>;
|
|
511
|
-
declare function
|
|
520
|
+
declare function verifyAndExtractSession(token: string, secret: string): Promise<AgriCoreSession>;
|
|
512
521
|
|
|
513
|
-
export { ACCESS_RANK, ACT, AccessLevel, type AccessLevelType, type AgriCoreSession, AgriLogger, AppError, ApprovalStatus, ApprovalType, AuditStatus, AuditType, type AuthScopeFields, type AvailableScope, CAT, type CompanyConfig, Converter, CropCycleStatus, ERROR_DICTIONARY, EmitterType, EnabledModule, ErrorFactory, FORM_REGISTRY, FacilityType, type FormMeta, HarvestMethod, type InfrastructureFields, LinkedStatus, MOD, MODULE_LABELS, MicroclimateZone, MimeType, type ModCode, NettingType, NotificationMode, OrderStatus, type PermissionLevel, PhysicalState, PlantingMethod, RESOURCE_MODULE_MAP, RESOURCE_PATHS, ROLE_SECTIONS_BY_KEY, RelationshipType, type ResolvedError, type ResourceOverrides, RowOrientation, SENTINEL_UUID, type ScopeType, SoilTexture, TASK_TYPE_REGISTRY, type TabConfig, TargetEntityType, TaskStatus, TaskType, type TaskTypeConfig, TransactionType, TrellisType, UserPrivilege, UserRole, type UserRoleType, Validator, VigorRating, WaterSource, assertCanWrite, assertTenantBoundary, buildRlsFilter, canRead, canWrite, evaluateBaseAccess, extractAppCode, generateCanonicalId, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getTaskTypeOptions, handleAppErrorResponse, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolveMenuVisibility, withAgriLogging };
|
|
522
|
+
export { ACCESS_RANK, ACT, AccessLevel, type AccessLevelType, type AgriCoreSession, AgriLogger, AppError, ApprovalStatus, ApprovalType, AuditStatus, AuditType, type AuthScopeFields, type AvailableScope, CAT, type CompanyConfig, Converter, CropCycleStatus, ERROR_DICTIONARY, EVERYONE, EmitterType, EnabledModule, ErrorFactory, FORM_REGISTRY, FacilityType, type FormMeta, HarvestMethod, type InfrastructureFields, LinkedStatus, MOD, MODULE_LABELS, MicroclimateZone, MimeType, type ModCode, NettingType, NotificationMode, OrderStatus, type PermissionLevel, PhysicalState, PlantingMethod, RESOURCE_MODULE_MAP, RESOURCE_PATHS, RESOURCE_REQUIREMENTS, ROLE_SECTIONS_BY_KEY, RelationshipType, type ResolvedError, type ResourceOverrides, type ResourceRequirement, RowOrientation, SENTINEL_UUID, type ScopeType, SoilTexture, TASK_TYPE_REGISTRY, type TabConfig, TargetEntityType, TaskStatus, TaskType, type TaskTypeConfig, TransactionType, TrellisType, UserPrivilege, UserRole, type UserRoleType, Validator, VigorRating, WaterSource, assertCanWrite, assertTenantBoundary, buildRlsFilter, canRead, canWrite, evaluateBaseAccess, extractAppCode, findSpecificOverride, generateCanonicalId, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getTaskTypeOptions, handleAppErrorResponse, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolveMenuVisibility, verifyAndExtractSession, withAgriLogging };
|
package/dist/index.d.ts
CHANGED
|
@@ -490,24 +490,33 @@ declare const RESOURCE_PATHS: {
|
|
|
490
490
|
declare const RESOURCE_MODULE_MAP: Record<string, string[]>;
|
|
491
491
|
declare const ACCESS_RANK: Record<AccessLevelType, number>;
|
|
492
492
|
declare const ROLE_SECTIONS_BY_KEY: Record<string, UserRoleType[]>;
|
|
493
|
+
interface ResourceRequirement {
|
|
494
|
+
allowedRoles?: UserRoleType[];
|
|
495
|
+
requiredPrivileges?: string[];
|
|
496
|
+
}
|
|
497
|
+
declare const EVERYONE: ("owner" | "manager" | "supervisor" | "leadingHand" | "worker")[];
|
|
498
|
+
declare const RESOURCE_REQUIREMENTS: Record<string, ResourceRequirement>;
|
|
493
499
|
declare function evaluateBaseAccess(resourcePath: string, session: {
|
|
494
500
|
role?: UserRoleType;
|
|
495
501
|
privileges?: string[];
|
|
496
502
|
enabledModules?: string[];
|
|
497
|
-
}, requirements?:
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
503
|
+
}, requirements?: ResourceRequirement): AccessLevelType;
|
|
504
|
+
/**
|
|
505
|
+
* The Judge: Overrides win over base access (Specificity principle).
|
|
506
|
+
*/
|
|
507
|
+
declare function resolveEffectiveAccess(base: AccessLevelType, override?: AccessLevelType | null): AccessLevelType;
|
|
508
|
+
declare function findSpecificOverride(session: AgriCoreSession, activeScopeId: string, resourcePath: string): AccessLevelType | null;
|
|
501
509
|
/**
|
|
502
510
|
* Tenant Boundary Guard (Backend Only)
|
|
503
511
|
* Note: db is typed as 'any' here because Base44 SDK types vary,
|
|
504
512
|
* but you can refine this with your internal SDK types.
|
|
505
513
|
*/
|
|
506
|
-
declare function assertTenantBoundary(db: any,
|
|
514
|
+
declare function assertTenantBoundary(db: any, session: AgriCoreSession, // Pass the JWT session instead of just requesterCompanyId
|
|
515
|
+
targets: {
|
|
507
516
|
targetUserId?: string;
|
|
508
517
|
authScopeId?: string;
|
|
509
518
|
expectedScopeType?: 'company' | 'legalEntity' | 'facility';
|
|
510
519
|
}): Promise<boolean>;
|
|
511
|
-
declare function
|
|
520
|
+
declare function verifyAndExtractSession(token: string, secret: string): Promise<AgriCoreSession>;
|
|
512
521
|
|
|
513
|
-
export { ACCESS_RANK, ACT, AccessLevel, type AccessLevelType, type AgriCoreSession, AgriLogger, AppError, ApprovalStatus, ApprovalType, AuditStatus, AuditType, type AuthScopeFields, type AvailableScope, CAT, type CompanyConfig, Converter, CropCycleStatus, ERROR_DICTIONARY, EmitterType, EnabledModule, ErrorFactory, FORM_REGISTRY, FacilityType, type FormMeta, HarvestMethod, type InfrastructureFields, LinkedStatus, MOD, MODULE_LABELS, MicroclimateZone, MimeType, type ModCode, NettingType, NotificationMode, OrderStatus, type PermissionLevel, PhysicalState, PlantingMethod, RESOURCE_MODULE_MAP, RESOURCE_PATHS, ROLE_SECTIONS_BY_KEY, RelationshipType, type ResolvedError, type ResourceOverrides, RowOrientation, SENTINEL_UUID, type ScopeType, SoilTexture, TASK_TYPE_REGISTRY, type TabConfig, TargetEntityType, TaskStatus, TaskType, type TaskTypeConfig, TransactionType, TrellisType, UserPrivilege, UserRole, type UserRoleType, Validator, VigorRating, WaterSource, assertCanWrite, assertTenantBoundary, buildRlsFilter, canRead, canWrite, evaluateBaseAccess, extractAppCode, generateCanonicalId, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getTaskTypeOptions, handleAppErrorResponse, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolveMenuVisibility, withAgriLogging };
|
|
522
|
+
export { ACCESS_RANK, ACT, AccessLevel, type AccessLevelType, type AgriCoreSession, AgriLogger, AppError, ApprovalStatus, ApprovalType, AuditStatus, AuditType, type AuthScopeFields, type AvailableScope, CAT, type CompanyConfig, Converter, CropCycleStatus, ERROR_DICTIONARY, EVERYONE, EmitterType, EnabledModule, ErrorFactory, FORM_REGISTRY, FacilityType, type FormMeta, HarvestMethod, type InfrastructureFields, LinkedStatus, MOD, MODULE_LABELS, MicroclimateZone, MimeType, type ModCode, NettingType, NotificationMode, OrderStatus, type PermissionLevel, PhysicalState, PlantingMethod, RESOURCE_MODULE_MAP, RESOURCE_PATHS, RESOURCE_REQUIREMENTS, ROLE_SECTIONS_BY_KEY, RelationshipType, type ResolvedError, type ResourceOverrides, type ResourceRequirement, RowOrientation, SENTINEL_UUID, type ScopeType, SoilTexture, TASK_TYPE_REGISTRY, type TabConfig, TargetEntityType, TaskStatus, TaskType, type TaskTypeConfig, TransactionType, TrellisType, UserPrivilege, UserRole, type UserRoleType, Validator, VigorRating, WaterSource, assertCanWrite, assertTenantBoundary, buildRlsFilter, canRead, canWrite, evaluateBaseAccess, extractAppCode, findSpecificOverride, generateCanonicalId, generateInfrastructureFields, getAcceptedMimeTypes, getApproveAction, getFormMeta, getFormsGroupedByModule, getTaskTypeOptions, handleAppErrorResponse, isTabVisible, isTabWritable, resolveAccess, resolveAppError, resolveEffectiveAccess, resolveError, resolveMenuVisibility, verifyAndExtractSession, withAgriLogging };
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
Converter: () => Converter,
|
|
34
34
|
CropCycleStatus: () => CropCycleStatus,
|
|
35
35
|
ERROR_DICTIONARY: () => ERROR_DICTIONARY,
|
|
36
|
+
EVERYONE: () => EVERYONE,
|
|
36
37
|
EmitterType: () => EmitterType,
|
|
37
38
|
EnabledModule: () => EnabledModule,
|
|
38
39
|
ErrorFactory: () => ErrorFactory,
|
|
@@ -51,6 +52,7 @@ __export(index_exports, {
|
|
|
51
52
|
PlantingMethod: () => PlantingMethod,
|
|
52
53
|
RESOURCE_MODULE_MAP: () => RESOURCE_MODULE_MAP,
|
|
53
54
|
RESOURCE_PATHS: () => RESOURCE_PATHS,
|
|
55
|
+
RESOURCE_REQUIREMENTS: () => RESOURCE_REQUIREMENTS,
|
|
54
56
|
ROLE_SECTIONS_BY_KEY: () => ROLE_SECTIONS_BY_KEY,
|
|
55
57
|
RelationshipType: () => RelationshipType,
|
|
56
58
|
RowOrientation: () => RowOrientation,
|
|
@@ -74,6 +76,7 @@ __export(index_exports, {
|
|
|
74
76
|
canWrite: () => canWrite,
|
|
75
77
|
evaluateBaseAccess: () => evaluateBaseAccess,
|
|
76
78
|
extractAppCode: () => extractAppCode,
|
|
79
|
+
findSpecificOverride: () => findSpecificOverride,
|
|
77
80
|
generateCanonicalId: () => generateCanonicalId,
|
|
78
81
|
generateInfrastructureFields: () => generateInfrastructureFields,
|
|
79
82
|
getAcceptedMimeTypes: () => getAcceptedMimeTypes,
|
|
@@ -89,9 +92,11 @@ __export(index_exports, {
|
|
|
89
92
|
resolveEffectiveAccess: () => resolveEffectiveAccess,
|
|
90
93
|
resolveError: () => resolveError,
|
|
91
94
|
resolveMenuVisibility: () => resolveMenuVisibility,
|
|
95
|
+
verifyAndExtractSession: () => verifyAndExtractSession,
|
|
92
96
|
withAgriLogging: () => withAgriLogging
|
|
93
97
|
});
|
|
94
98
|
module.exports = __toCommonJS(index_exports);
|
|
99
|
+
var import_jose = require("jose");
|
|
95
100
|
|
|
96
101
|
// src/logger.ts
|
|
97
102
|
var SENSITIVE_KEYS = ["password", "token", "secret", "apiKey", "creditCard", "authorization"];
|
|
@@ -430,7 +435,7 @@ function generateCanonicalId() {
|
|
|
430
435
|
}
|
|
431
436
|
function generateInfrastructureFields(activeScope, availableScopes) {
|
|
432
437
|
if (!activeScope || !availableScopes?.length) {
|
|
433
|
-
throw
|
|
438
|
+
throw ErrorFactory.unauthorized("[generateInfrastructureFields] Missing scope context.");
|
|
434
439
|
}
|
|
435
440
|
let companyId = null;
|
|
436
441
|
let legalEntityId = null;
|
|
@@ -444,7 +449,7 @@ function generateInfrastructureFields(activeScope, availableScopes) {
|
|
|
444
449
|
pointer = parentId ? availableScopes.find((s) => s.base44Id === parentId) || null : null;
|
|
445
450
|
}
|
|
446
451
|
if (!companyId) {
|
|
447
|
-
throw
|
|
452
|
+
throw ErrorFactory.unauthorized("[generateInfrastructureFields] Could not resolve companyId from scope tree.");
|
|
448
453
|
}
|
|
449
454
|
return {
|
|
450
455
|
canonicalId: crypto.randomUUID(),
|
|
@@ -907,6 +912,76 @@ var ROLE_SECTIONS_BY_KEY = {
|
|
|
907
912
|
records: [UserRole.OWNER, UserRole.MANAGER],
|
|
908
913
|
adminSettings: []
|
|
909
914
|
};
|
|
915
|
+
var EVERYONE = Object.values(UserRole);
|
|
916
|
+
var RESOURCE_REQUIREMENTS = {
|
|
917
|
+
[RESOURCE_PATHS.dashboard]: {
|
|
918
|
+
allowedRoles: EVERYONE
|
|
919
|
+
},
|
|
920
|
+
[RESOURCE_PATHS.operations]: {
|
|
921
|
+
allowedRoles: EVERYONE
|
|
922
|
+
},
|
|
923
|
+
// [RESOURCE_PATHS.equipment]: EVERYONE,
|
|
924
|
+
// [RESOURCE_PATHS.safety]: ALL_OPERATIONS,
|
|
925
|
+
// [RESOURCE_PATHS.team]: ALL_OPERATIONS,
|
|
926
|
+
// [RESOURCE_PATHS.records]: ALL_OPERATIONS,
|
|
927
|
+
// [RESOURCE_PATHS.adminSettings]: ALL_OPERATIONS,
|
|
928
|
+
// ── Records & Reports ──────────────────────────────────────────────────────
|
|
929
|
+
[RESOURCE_PATHS.documentHub]: {
|
|
930
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
931
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
932
|
+
},
|
|
933
|
+
[RESOURCE_PATHS.documentHubTemplates]: {
|
|
934
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
935
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
936
|
+
},
|
|
937
|
+
[RESOURCE_PATHS.documentHubFolder]: {
|
|
938
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
939
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
940
|
+
},
|
|
941
|
+
[RESOURCE_PATHS.documentHubExtraction]: {
|
|
942
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
943
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
944
|
+
},
|
|
945
|
+
[RESOURCE_PATHS.documentHubQueue]: {
|
|
946
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
947
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
948
|
+
},
|
|
949
|
+
// [RESOURCE_PATHS.analytics]: ALL_OPERATIONS,
|
|
950
|
+
// ── Admin Settings ─────────────────────────────────────────────────────────
|
|
951
|
+
[RESOURCE_PATHS.adminUserRoster]: {
|
|
952
|
+
allowedRoles: [UserRole.OWNER],
|
|
953
|
+
requiredPrivileges: [UserPrivilege.ADMIN]
|
|
954
|
+
},
|
|
955
|
+
[RESOURCE_PATHS.adminBlueprintsSites]: {
|
|
956
|
+
allowedRoles: [UserRole.OWNER],
|
|
957
|
+
requiredPrivileges: [UserPrivilege.ADMIN]
|
|
958
|
+
},
|
|
959
|
+
[RESOURCE_PATHS.adminRolesPermissions]: {
|
|
960
|
+
allowedRoles: [UserRole.OWNER],
|
|
961
|
+
requiredPrivileges: [UserPrivilege.ADMIN]
|
|
962
|
+
},
|
|
963
|
+
[RESOURCE_PATHS.adminCompanySettings]: {
|
|
964
|
+
allowedRoles: [UserRole.OWNER]
|
|
965
|
+
}
|
|
966
|
+
// // ── Operations ────────────────────────────────────────────────────────────
|
|
967
|
+
// [RESOURCE_PATHS.farmManagement]: [EnabledModule.FARM_MANAGEMENT],
|
|
968
|
+
// [RESOURCE_PATHS.packhouse]: [EnabledModule.PACKHOUSE],
|
|
969
|
+
// [RESOURCE_PATHS.accommodation]: [EnabledModule.ACCOMMODATION],
|
|
970
|
+
// // ── Equipment & Fleet ──────────────────────────────────────────────────────
|
|
971
|
+
// [RESOURCE_PATHS.equipPreStart]: ALL_OPERATIONS,
|
|
972
|
+
// [RESOURCE_PATHS.equipMaintenance]: ALL_OPERATIONS,
|
|
973
|
+
// [RESOURCE_PATHS.equipFuelRegister]: ALL_OPERATIONS,
|
|
974
|
+
// [RESOURCE_PATHS.equipCalibration]: AGRI_ONLY,
|
|
975
|
+
// [RESOURCE_PATHS.equipFleet]: ALL_OPERATIONS,
|
|
976
|
+
// // ── Safety, Quality & Compliance ──────────────────────────────────────────
|
|
977
|
+
// [RESOURCE_PATHS.safetyAuditing]: ALL_OPERATIONS,
|
|
978
|
+
// [RESOURCE_PATHS.safetyFood]: AGRI_ONLY,
|
|
979
|
+
// [RESOURCE_PATHS.safetyWhs]: ALL_OPERATIONS,
|
|
980
|
+
// [RESOURCE_PATHS.safetyCleaning]: ALL_OPERATIONS,
|
|
981
|
+
// // ── Team & HR ──────────────────────────────────────────────────────────────
|
|
982
|
+
// [RESOURCE_PATHS.teamStaff]: ALL_OPERATIONS,
|
|
983
|
+
// [RESOURCE_PATHS.teamTraining]: ALL_OPERATIONS,
|
|
984
|
+
};
|
|
910
985
|
function evaluateBaseAccess(resourcePath, session, requirements = {}) {
|
|
911
986
|
if (!session?.role) return "NONE";
|
|
912
987
|
if (session.role === UserRole.OWNER) return "WRITE";
|
|
@@ -925,44 +1000,63 @@ function evaluateBaseAccess(resourcePath, session, requirements = {}) {
|
|
|
925
1000
|
}
|
|
926
1001
|
return session.role === UserRole.MANAGER ? "WRITE" : "READ";
|
|
927
1002
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1003
|
+
function resolveEffectiveAccess(base, override) {
|
|
1004
|
+
return override === void 0 || override === null ? base : override;
|
|
1005
|
+
}
|
|
1006
|
+
function findSpecificOverride(session, activeScopeId, resourcePath) {
|
|
1007
|
+
const scopeOverrides = session.resourceOverrides?.[activeScopeId];
|
|
1008
|
+
if (!scopeOverrides) return null;
|
|
1009
|
+
const parts = resourcePath.split(".");
|
|
1010
|
+
let currentPath = "";
|
|
1011
|
+
let bestOverride = null;
|
|
1012
|
+
for (const part of parts) {
|
|
1013
|
+
currentPath = currentPath ? `${currentPath}.${part}` : part;
|
|
1014
|
+
if (scopeOverrides[currentPath] !== void 0) {
|
|
1015
|
+
bestOverride = scopeOverrides[currentPath];
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return bestOverride;
|
|
1019
|
+
}
|
|
1020
|
+
async function assertTenantBoundary(db, session, targets) {
|
|
1021
|
+
if (!session.companyId) {
|
|
1022
|
+
throw ErrorFactory.internal(MOD.ADMIN, "Missing requester company context.");
|
|
931
1023
|
}
|
|
932
1024
|
if (targets.targetUserId) {
|
|
933
1025
|
const targetUserAssignments = await db.UserRoleAssignment.filter({
|
|
934
1026
|
userId: targets.targetUserId,
|
|
935
|
-
companyId:
|
|
1027
|
+
companyId: session.companyId
|
|
936
1028
|
});
|
|
937
1029
|
if (targetUserAssignments.length === 0) {
|
|
938
|
-
console.error(`[SECURITY BREACH
|
|
939
|
-
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid target user
|
|
1030
|
+
console.error(`[SECURITY BREACH] User tried to modify targetUserId ${targets.targetUserId}`);
|
|
1031
|
+
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid target user.");
|
|
940
1032
|
}
|
|
941
1033
|
}
|
|
942
1034
|
if (targets.authScopeId) {
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
const le = await db.LegalEntity.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId });
|
|
950
|
-
if (le.length > 0) matchFound = true;
|
|
951
|
-
}
|
|
952
|
-
if (!matchFound && (targets.expectedScopeType === "facility" || !targets.expectedScopeType)) {
|
|
953
|
-
const fac = await db.Facility.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId });
|
|
954
|
-
if (fac.length > 0) matchFound = true;
|
|
1035
|
+
const matchedScope = session.availableScopes?.find(
|
|
1036
|
+
(scope) => scope.authScopeId === targets.authScopeId
|
|
1037
|
+
);
|
|
1038
|
+
if (!matchedScope) {
|
|
1039
|
+
console.error(`[SECURITY BREACH] Scope ${targets.authScopeId} not found in user's permitted list.`);
|
|
1040
|
+
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Tenant boundary violation.");
|
|
955
1041
|
}
|
|
956
|
-
if (
|
|
957
|
-
console.error(`[
|
|
958
|
-
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid authorization scope level
|
|
1042
|
+
if (targets.expectedScopeType && matchedScope.type !== targets.expectedScopeType) {
|
|
1043
|
+
console.error(`[VALIDATION ERROR] Expected type ${targets.expectedScopeType} but got ${matchedScope.type}`);
|
|
1044
|
+
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid authorization scope level.");
|
|
959
1045
|
}
|
|
960
1046
|
}
|
|
961
1047
|
return true;
|
|
962
1048
|
}
|
|
963
|
-
function
|
|
964
|
-
if (
|
|
965
|
-
|
|
1049
|
+
async function verifyAndExtractSession(token, secret) {
|
|
1050
|
+
if (!token) {
|
|
1051
|
+
throw ErrorFactory.unauthorized(MOD.SESSION, "UNAUTHORIZED: No secure session token provided.");
|
|
1052
|
+
}
|
|
1053
|
+
try {
|
|
1054
|
+
const secretKey = new TextEncoder().encode(secret);
|
|
1055
|
+
const { payload } = await (0, import_jose.jwtVerify)(token, secretKey);
|
|
1056
|
+
return payload;
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
throw ErrorFactory.unauthorized(MOD.SESSION, "UNAUTHORIZED: No secure session token provided.");
|
|
1059
|
+
}
|
|
966
1060
|
}
|
|
967
1061
|
// Annotate the CommonJS export names for ESM import in node:
|
|
968
1062
|
0 && (module.exports = {
|
|
@@ -979,6 +1073,7 @@ function resolveEffectiveAccess(base, override) {
|
|
|
979
1073
|
Converter,
|
|
980
1074
|
CropCycleStatus,
|
|
981
1075
|
ERROR_DICTIONARY,
|
|
1076
|
+
EVERYONE,
|
|
982
1077
|
EmitterType,
|
|
983
1078
|
EnabledModule,
|
|
984
1079
|
ErrorFactory,
|
|
@@ -997,6 +1092,7 @@ function resolveEffectiveAccess(base, override) {
|
|
|
997
1092
|
PlantingMethod,
|
|
998
1093
|
RESOURCE_MODULE_MAP,
|
|
999
1094
|
RESOURCE_PATHS,
|
|
1095
|
+
RESOURCE_REQUIREMENTS,
|
|
1000
1096
|
ROLE_SECTIONS_BY_KEY,
|
|
1001
1097
|
RelationshipType,
|
|
1002
1098
|
RowOrientation,
|
|
@@ -1020,6 +1116,7 @@ function resolveEffectiveAccess(base, override) {
|
|
|
1020
1116
|
canWrite,
|
|
1021
1117
|
evaluateBaseAccess,
|
|
1022
1118
|
extractAppCode,
|
|
1119
|
+
findSpecificOverride,
|
|
1023
1120
|
generateCanonicalId,
|
|
1024
1121
|
generateInfrastructureFields,
|
|
1025
1122
|
getAcceptedMimeTypes,
|
|
@@ -1035,5 +1132,6 @@ function resolveEffectiveAccess(base, override) {
|
|
|
1035
1132
|
resolveEffectiveAccess,
|
|
1036
1133
|
resolveError,
|
|
1037
1134
|
resolveMenuVisibility,
|
|
1135
|
+
verifyAndExtractSession,
|
|
1038
1136
|
withAgriLogging
|
|
1039
1137
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { jwtVerify } from "jose";
|
|
3
|
+
|
|
1
4
|
// src/logger.ts
|
|
2
5
|
var SENSITIVE_KEYS = ["password", "token", "secret", "apiKey", "creditCard", "authorization"];
|
|
3
6
|
function redact(obj) {
|
|
@@ -335,7 +338,7 @@ function generateCanonicalId() {
|
|
|
335
338
|
}
|
|
336
339
|
function generateInfrastructureFields(activeScope, availableScopes) {
|
|
337
340
|
if (!activeScope || !availableScopes?.length) {
|
|
338
|
-
throw
|
|
341
|
+
throw ErrorFactory.unauthorized("[generateInfrastructureFields] Missing scope context.");
|
|
339
342
|
}
|
|
340
343
|
let companyId = null;
|
|
341
344
|
let legalEntityId = null;
|
|
@@ -349,7 +352,7 @@ function generateInfrastructureFields(activeScope, availableScopes) {
|
|
|
349
352
|
pointer = parentId ? availableScopes.find((s) => s.base44Id === parentId) || null : null;
|
|
350
353
|
}
|
|
351
354
|
if (!companyId) {
|
|
352
|
-
throw
|
|
355
|
+
throw ErrorFactory.unauthorized("[generateInfrastructureFields] Could not resolve companyId from scope tree.");
|
|
353
356
|
}
|
|
354
357
|
return {
|
|
355
358
|
canonicalId: crypto.randomUUID(),
|
|
@@ -812,6 +815,76 @@ var ROLE_SECTIONS_BY_KEY = {
|
|
|
812
815
|
records: [UserRole.OWNER, UserRole.MANAGER],
|
|
813
816
|
adminSettings: []
|
|
814
817
|
};
|
|
818
|
+
var EVERYONE = Object.values(UserRole);
|
|
819
|
+
var RESOURCE_REQUIREMENTS = {
|
|
820
|
+
[RESOURCE_PATHS.dashboard]: {
|
|
821
|
+
allowedRoles: EVERYONE
|
|
822
|
+
},
|
|
823
|
+
[RESOURCE_PATHS.operations]: {
|
|
824
|
+
allowedRoles: EVERYONE
|
|
825
|
+
},
|
|
826
|
+
// [RESOURCE_PATHS.equipment]: EVERYONE,
|
|
827
|
+
// [RESOURCE_PATHS.safety]: ALL_OPERATIONS,
|
|
828
|
+
// [RESOURCE_PATHS.team]: ALL_OPERATIONS,
|
|
829
|
+
// [RESOURCE_PATHS.records]: ALL_OPERATIONS,
|
|
830
|
+
// [RESOURCE_PATHS.adminSettings]: ALL_OPERATIONS,
|
|
831
|
+
// ── Records & Reports ──────────────────────────────────────────────────────
|
|
832
|
+
[RESOURCE_PATHS.documentHub]: {
|
|
833
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
834
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
835
|
+
},
|
|
836
|
+
[RESOURCE_PATHS.documentHubTemplates]: {
|
|
837
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
838
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
839
|
+
},
|
|
840
|
+
[RESOURCE_PATHS.documentHubFolder]: {
|
|
841
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
842
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
843
|
+
},
|
|
844
|
+
[RESOURCE_PATHS.documentHubExtraction]: {
|
|
845
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
846
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
847
|
+
},
|
|
848
|
+
[RESOURCE_PATHS.documentHubQueue]: {
|
|
849
|
+
allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
|
|
850
|
+
requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.HR]
|
|
851
|
+
},
|
|
852
|
+
// [RESOURCE_PATHS.analytics]: ALL_OPERATIONS,
|
|
853
|
+
// ── Admin Settings ─────────────────────────────────────────────────────────
|
|
854
|
+
[RESOURCE_PATHS.adminUserRoster]: {
|
|
855
|
+
allowedRoles: [UserRole.OWNER],
|
|
856
|
+
requiredPrivileges: [UserPrivilege.ADMIN]
|
|
857
|
+
},
|
|
858
|
+
[RESOURCE_PATHS.adminBlueprintsSites]: {
|
|
859
|
+
allowedRoles: [UserRole.OWNER],
|
|
860
|
+
requiredPrivileges: [UserPrivilege.ADMIN]
|
|
861
|
+
},
|
|
862
|
+
[RESOURCE_PATHS.adminRolesPermissions]: {
|
|
863
|
+
allowedRoles: [UserRole.OWNER],
|
|
864
|
+
requiredPrivileges: [UserPrivilege.ADMIN]
|
|
865
|
+
},
|
|
866
|
+
[RESOURCE_PATHS.adminCompanySettings]: {
|
|
867
|
+
allowedRoles: [UserRole.OWNER]
|
|
868
|
+
}
|
|
869
|
+
// // ── Operations ────────────────────────────────────────────────────────────
|
|
870
|
+
// [RESOURCE_PATHS.farmManagement]: [EnabledModule.FARM_MANAGEMENT],
|
|
871
|
+
// [RESOURCE_PATHS.packhouse]: [EnabledModule.PACKHOUSE],
|
|
872
|
+
// [RESOURCE_PATHS.accommodation]: [EnabledModule.ACCOMMODATION],
|
|
873
|
+
// // ── Equipment & Fleet ──────────────────────────────────────────────────────
|
|
874
|
+
// [RESOURCE_PATHS.equipPreStart]: ALL_OPERATIONS,
|
|
875
|
+
// [RESOURCE_PATHS.equipMaintenance]: ALL_OPERATIONS,
|
|
876
|
+
// [RESOURCE_PATHS.equipFuelRegister]: ALL_OPERATIONS,
|
|
877
|
+
// [RESOURCE_PATHS.equipCalibration]: AGRI_ONLY,
|
|
878
|
+
// [RESOURCE_PATHS.equipFleet]: ALL_OPERATIONS,
|
|
879
|
+
// // ── Safety, Quality & Compliance ──────────────────────────────────────────
|
|
880
|
+
// [RESOURCE_PATHS.safetyAuditing]: ALL_OPERATIONS,
|
|
881
|
+
// [RESOURCE_PATHS.safetyFood]: AGRI_ONLY,
|
|
882
|
+
// [RESOURCE_PATHS.safetyWhs]: ALL_OPERATIONS,
|
|
883
|
+
// [RESOURCE_PATHS.safetyCleaning]: ALL_OPERATIONS,
|
|
884
|
+
// // ── Team & HR ──────────────────────────────────────────────────────────────
|
|
885
|
+
// [RESOURCE_PATHS.teamStaff]: ALL_OPERATIONS,
|
|
886
|
+
// [RESOURCE_PATHS.teamTraining]: ALL_OPERATIONS,
|
|
887
|
+
};
|
|
815
888
|
function evaluateBaseAccess(resourcePath, session, requirements = {}) {
|
|
816
889
|
if (!session?.role) return "NONE";
|
|
817
890
|
if (session.role === UserRole.OWNER) return "WRITE";
|
|
@@ -830,44 +903,63 @@ function evaluateBaseAccess(resourcePath, session, requirements = {}) {
|
|
|
830
903
|
}
|
|
831
904
|
return session.role === UserRole.MANAGER ? "WRITE" : "READ";
|
|
832
905
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
906
|
+
function resolveEffectiveAccess(base, override) {
|
|
907
|
+
return override === void 0 || override === null ? base : override;
|
|
908
|
+
}
|
|
909
|
+
function findSpecificOverride(session, activeScopeId, resourcePath) {
|
|
910
|
+
const scopeOverrides = session.resourceOverrides?.[activeScopeId];
|
|
911
|
+
if (!scopeOverrides) return null;
|
|
912
|
+
const parts = resourcePath.split(".");
|
|
913
|
+
let currentPath = "";
|
|
914
|
+
let bestOverride = null;
|
|
915
|
+
for (const part of parts) {
|
|
916
|
+
currentPath = currentPath ? `${currentPath}.${part}` : part;
|
|
917
|
+
if (scopeOverrides[currentPath] !== void 0) {
|
|
918
|
+
bestOverride = scopeOverrides[currentPath];
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return bestOverride;
|
|
922
|
+
}
|
|
923
|
+
async function assertTenantBoundary(db, session, targets) {
|
|
924
|
+
if (!session.companyId) {
|
|
925
|
+
throw ErrorFactory.internal(MOD.ADMIN, "Missing requester company context.");
|
|
836
926
|
}
|
|
837
927
|
if (targets.targetUserId) {
|
|
838
928
|
const targetUserAssignments = await db.UserRoleAssignment.filter({
|
|
839
929
|
userId: targets.targetUserId,
|
|
840
|
-
companyId:
|
|
930
|
+
companyId: session.companyId
|
|
841
931
|
});
|
|
842
932
|
if (targetUserAssignments.length === 0) {
|
|
843
|
-
console.error(`[SECURITY BREACH
|
|
844
|
-
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid target user
|
|
933
|
+
console.error(`[SECURITY BREACH] User tried to modify targetUserId ${targets.targetUserId}`);
|
|
934
|
+
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid target user.");
|
|
845
935
|
}
|
|
846
936
|
}
|
|
847
937
|
if (targets.authScopeId) {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
const le = await db.LegalEntity.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId });
|
|
855
|
-
if (le.length > 0) matchFound = true;
|
|
856
|
-
}
|
|
857
|
-
if (!matchFound && (targets.expectedScopeType === "facility" || !targets.expectedScopeType)) {
|
|
858
|
-
const fac = await db.Facility.filter({ canonicalId: targets.authScopeId, companyId: requesterCompanyId });
|
|
859
|
-
if (fac.length > 0) matchFound = true;
|
|
938
|
+
const matchedScope = session.availableScopes?.find(
|
|
939
|
+
(scope) => scope.authScopeId === targets.authScopeId
|
|
940
|
+
);
|
|
941
|
+
if (!matchedScope) {
|
|
942
|
+
console.error(`[SECURITY BREACH] Scope ${targets.authScopeId} not found in user's permitted list.`);
|
|
943
|
+
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Tenant boundary violation.");
|
|
860
944
|
}
|
|
861
|
-
if (
|
|
862
|
-
console.error(`[
|
|
863
|
-
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid authorization scope level
|
|
945
|
+
if (targets.expectedScopeType && matchedScope.type !== targets.expectedScopeType) {
|
|
946
|
+
console.error(`[VALIDATION ERROR] Expected type ${targets.expectedScopeType} but got ${matchedScope.type}`);
|
|
947
|
+
throw ErrorFactory.forbidden(MOD.ADMIN, ACT.EDIT, "Invalid authorization scope level.");
|
|
864
948
|
}
|
|
865
949
|
}
|
|
866
950
|
return true;
|
|
867
951
|
}
|
|
868
|
-
function
|
|
869
|
-
if (
|
|
870
|
-
|
|
952
|
+
async function verifyAndExtractSession(token, secret) {
|
|
953
|
+
if (!token) {
|
|
954
|
+
throw ErrorFactory.unauthorized(MOD.SESSION, "UNAUTHORIZED: No secure session token provided.");
|
|
955
|
+
}
|
|
956
|
+
try {
|
|
957
|
+
const secretKey = new TextEncoder().encode(secret);
|
|
958
|
+
const { payload } = await jwtVerify(token, secretKey);
|
|
959
|
+
return payload;
|
|
960
|
+
} catch (error) {
|
|
961
|
+
throw ErrorFactory.unauthorized(MOD.SESSION, "UNAUTHORIZED: No secure session token provided.");
|
|
962
|
+
}
|
|
871
963
|
}
|
|
872
964
|
export {
|
|
873
965
|
ACCESS_RANK,
|
|
@@ -883,6 +975,7 @@ export {
|
|
|
883
975
|
Converter,
|
|
884
976
|
CropCycleStatus,
|
|
885
977
|
ERROR_DICTIONARY,
|
|
978
|
+
EVERYONE,
|
|
886
979
|
EmitterType,
|
|
887
980
|
EnabledModule,
|
|
888
981
|
ErrorFactory,
|
|
@@ -901,6 +994,7 @@ export {
|
|
|
901
994
|
PlantingMethod,
|
|
902
995
|
RESOURCE_MODULE_MAP,
|
|
903
996
|
RESOURCE_PATHS,
|
|
997
|
+
RESOURCE_REQUIREMENTS,
|
|
904
998
|
ROLE_SECTIONS_BY_KEY,
|
|
905
999
|
RelationshipType,
|
|
906
1000
|
RowOrientation,
|
|
@@ -924,6 +1018,7 @@ export {
|
|
|
924
1018
|
canWrite,
|
|
925
1019
|
evaluateBaseAccess,
|
|
926
1020
|
extractAppCode,
|
|
1021
|
+
findSpecificOverride,
|
|
927
1022
|
generateCanonicalId,
|
|
928
1023
|
generateInfrastructureFields,
|
|
929
1024
|
getAcceptedMimeTypes,
|
|
@@ -939,5 +1034,6 @@ export {
|
|
|
939
1034
|
resolveEffectiveAccess,
|
|
940
1035
|
resolveError,
|
|
941
1036
|
resolveMenuVisibility,
|
|
1037
|
+
verifyAndExtractSession,
|
|
942
1038
|
withAgriLogging
|
|
943
1039
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litusarchieve18/agricore-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Canonical Backend Utility Library for AgriCore",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -23,5 +23,7 @@
|
|
|
23
23
|
"tsup": "^8.0.0",
|
|
24
24
|
"typescript": "^5.0.0"
|
|
25
25
|
},
|
|
26
|
-
"dependencies": {
|
|
27
|
-
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"jose": "^6.2.2"
|
|
28
|
+
}
|
|
29
|
+
}
|