@fjell/core 4.4.49 → 4.4.51
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/event/emitter.d.ts +140 -0
- package/dist/event/events.d.ts +81 -0
- package/dist/event/index.d.ts +38 -0
- package/dist/event/matching.d.ts +54 -0
- package/dist/event/subscription.d.ts +74 -0
- package/dist/event/types.d.ts +186 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +272 -0
- package/package.json +3 -3
- package/src/AItemService.ts +0 -38
- package/src/Coordinate.ts +0 -35
- package/src/dictionary.ts +0 -84
- package/src/errors/ActionError.ts +0 -69
- package/src/errors/BusinessLogicError.ts +0 -24
- package/src/errors/DuplicateError.ts +0 -57
- package/src/errors/NotFoundError.ts +0 -24
- package/src/errors/PermissionError.ts +0 -31
- package/src/errors/ValidationError.ts +0 -27
- package/src/errors/index.ts +0 -7
- package/src/index.ts +0 -66
- package/src/item/IFactory.ts +0 -122
- package/src/item/IQFactory.ts +0 -163
- package/src/item/IQUtils.ts +0 -392
- package/src/item/IUtils.ts +0 -40
- package/src/item/ItemQuery.ts +0 -88
- package/src/items.ts +0 -120
- package/src/key/KUtils.ts +0 -484
- package/src/keys.ts +0 -95
- package/src/logger.ts +0 -5
- package/src/operations/OperationContext.ts +0 -12
- package/src/operations/Operations.ts +0 -357
- package/src/operations/contained.ts +0 -134
- package/src/operations/errorEnhancer.ts +0 -204
- package/src/operations/index.ts +0 -2
- package/src/operations/methods.ts +0 -363
- package/src/operations/primary.ts +0 -101
- package/src/operations/specialized.ts +0 -71
- package/src/operations/wrappers/createActionWrapper.ts +0 -108
- package/src/operations/wrappers/createAllActionWrapper.ts +0 -109
- package/src/operations/wrappers/createAllFacetWrapper.ts +0 -98
- package/src/operations/wrappers/createAllWrapper.ts +0 -103
- package/src/operations/wrappers/createCreateWrapper.ts +0 -117
- package/src/operations/wrappers/createFacetWrapper.ts +0 -97
- package/src/operations/wrappers/createFindOneWrapper.ts +0 -105
- package/src/operations/wrappers/createFindWrapper.ts +0 -105
- package/src/operations/wrappers/createGetWrapper.ts +0 -96
- package/src/operations/wrappers/createOneWrapper.ts +0 -128
- package/src/operations/wrappers/createRemoveWrapper.ts +0 -91
- package/src/operations/wrappers/createUpdateWrapper.ts +0 -106
- package/src/operations/wrappers/createUpsertWrapper.ts +0 -108
- package/src/operations/wrappers/index.ts +0 -39
- package/src/operations/wrappers/types.ts +0 -63
- package/src/validation/ItemValidator.ts +0 -131
- package/src/validation/KeyValidator.ts +0 -365
- package/src/validation/LocationValidator.ts +0 -136
- package/src/validation/QueryValidator.ts +0 -250
- package/src/validation/index.ts +0 -32
- package/src/validation/types.ts +0 -45
package/dist/index.js
CHANGED
|
@@ -1519,6 +1519,50 @@ function getErrorInfo(error) {
|
|
|
1519
1519
|
};
|
|
1520
1520
|
}
|
|
1521
1521
|
|
|
1522
|
+
// src/event/events.ts
|
|
1523
|
+
function isCreateEvent(event) {
|
|
1524
|
+
return event.eventType === "create";
|
|
1525
|
+
}
|
|
1526
|
+
function isUpdateEvent(event) {
|
|
1527
|
+
return event.eventType === "update";
|
|
1528
|
+
}
|
|
1529
|
+
function isDeleteEvent(event) {
|
|
1530
|
+
return event.eventType === "delete";
|
|
1531
|
+
}
|
|
1532
|
+
function isActionEvent(event) {
|
|
1533
|
+
return event.eventType === "action";
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
// src/event/subscription.ts
|
|
1537
|
+
function isItemSubscription(subscription) {
|
|
1538
|
+
return "key" in subscription;
|
|
1539
|
+
}
|
|
1540
|
+
function isLocationSubscription(subscription) {
|
|
1541
|
+
return "kta" in subscription && "location" in subscription;
|
|
1542
|
+
}
|
|
1543
|
+
function generateSubscriptionId() {
|
|
1544
|
+
return `sub-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
1545
|
+
}
|
|
1546
|
+
function createItemSubscription(key, options) {
|
|
1547
|
+
return {
|
|
1548
|
+
id: generateSubscriptionId(),
|
|
1549
|
+
key,
|
|
1550
|
+
eventTypes: options?.eventTypes,
|
|
1551
|
+
scopes: options?.scopes,
|
|
1552
|
+
query: options?.query
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
function createLocationSubscription(kta, location, options) {
|
|
1556
|
+
return {
|
|
1557
|
+
id: generateSubscriptionId(),
|
|
1558
|
+
kta,
|
|
1559
|
+
location,
|
|
1560
|
+
eventTypes: options?.eventTypes,
|
|
1561
|
+
scopes: options?.scopes,
|
|
1562
|
+
query: options?.query
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1522
1566
|
// src/operations/Operations.ts
|
|
1523
1567
|
function isPriKey2(key) {
|
|
1524
1568
|
return !("loc" in key) || !key.loc;
|
|
@@ -1527,6 +1571,203 @@ function isComKey2(key) {
|
|
|
1527
1571
|
return "loc" in key && key.loc && Array.isArray(key.loc) && key.loc.length > 0;
|
|
1528
1572
|
}
|
|
1529
1573
|
|
|
1574
|
+
// src/event/matching.ts
|
|
1575
|
+
function doesEventMatchSubscription(event, subscription) {
|
|
1576
|
+
if (!doesScopeMatch(event.scopes, subscription.scopes)) {
|
|
1577
|
+
return false;
|
|
1578
|
+
}
|
|
1579
|
+
if (!doesEventTypeMatch(event.eventType, subscription.eventTypes)) {
|
|
1580
|
+
return false;
|
|
1581
|
+
}
|
|
1582
|
+
if (isItemSubscription(subscription)) {
|
|
1583
|
+
return doesKeyMatch(event.key, subscription.key);
|
|
1584
|
+
} else if (isLocationSubscription(subscription)) {
|
|
1585
|
+
return doesKeyMatchLocation(event.key, subscription.kta, subscription.location);
|
|
1586
|
+
}
|
|
1587
|
+
return false;
|
|
1588
|
+
}
|
|
1589
|
+
function doesScopeMatch(eventScopes, subscriptionScopes) {
|
|
1590
|
+
if (!subscriptionScopes || subscriptionScopes.length === 0) {
|
|
1591
|
+
return true;
|
|
1592
|
+
}
|
|
1593
|
+
return subscriptionScopes.some(
|
|
1594
|
+
(requiredScope) => eventScopes.includes(requiredScope)
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
function doesEventTypeMatch(eventType, subscriptionEventTypes) {
|
|
1598
|
+
if (!subscriptionEventTypes || subscriptionEventTypes.length === 0) {
|
|
1599
|
+
return true;
|
|
1600
|
+
}
|
|
1601
|
+
return subscriptionEventTypes.includes(eventType);
|
|
1602
|
+
}
|
|
1603
|
+
function doesKeyMatch(eventKey, subscriptionKey) {
|
|
1604
|
+
if (isPriKey2(eventKey) && isPriKey2(subscriptionKey)) {
|
|
1605
|
+
return eventKey.pk === subscriptionKey.pk && eventKey.kt === subscriptionKey.kt;
|
|
1606
|
+
}
|
|
1607
|
+
if (isComKey2(eventKey) && isComKey2(subscriptionKey)) {
|
|
1608
|
+
const eventComKey = eventKey;
|
|
1609
|
+
const subscriptionComKey = subscriptionKey;
|
|
1610
|
+
if (eventComKey.pk !== subscriptionComKey.pk || eventComKey.kt !== subscriptionComKey.kt) {
|
|
1611
|
+
return false;
|
|
1612
|
+
}
|
|
1613
|
+
if (eventComKey.loc.length !== subscriptionComKey.loc.length) {
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
return eventComKey.loc.every((eventLocKey, index) => {
|
|
1617
|
+
const subLocKey = subscriptionComKey.loc[index];
|
|
1618
|
+
return eventLocKey.lk === subLocKey.lk && eventLocKey.kt === subLocKey.kt;
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
return false;
|
|
1622
|
+
}
|
|
1623
|
+
function doesKeyMatchLocation(eventKey, subscriptionKta, subscriptionLocation) {
|
|
1624
|
+
const targetItemType = subscriptionKta[subscriptionKta.length - 1];
|
|
1625
|
+
if (eventKey.kt !== targetItemType) {
|
|
1626
|
+
return false;
|
|
1627
|
+
}
|
|
1628
|
+
if (isPriKey2(eventKey)) {
|
|
1629
|
+
return subscriptionLocation.length === 0;
|
|
1630
|
+
}
|
|
1631
|
+
if (isComKey2(eventKey)) {
|
|
1632
|
+
const comKey = eventKey;
|
|
1633
|
+
return doesLocationMatch(comKey.loc, subscriptionLocation, subscriptionKta);
|
|
1634
|
+
}
|
|
1635
|
+
return false;
|
|
1636
|
+
}
|
|
1637
|
+
function doesLocationMatch(eventLocation, subscriptionLocation, _subscriptionKta) {
|
|
1638
|
+
if (subscriptionLocation.length === 0) {
|
|
1639
|
+
return true;
|
|
1640
|
+
}
|
|
1641
|
+
if (eventLocation.length < subscriptionLocation.length) {
|
|
1642
|
+
return false;
|
|
1643
|
+
}
|
|
1644
|
+
for (let i = 0; i < subscriptionLocation.length; i++) {
|
|
1645
|
+
const eventLocKey = eventLocation[i];
|
|
1646
|
+
const subLocKey = subscriptionLocation[i];
|
|
1647
|
+
if (!eventLocKey || !subLocKey) {
|
|
1648
|
+
return false;
|
|
1649
|
+
}
|
|
1650
|
+
if (eventLocKey.lk !== subLocKey.lk || eventLocKey.kt !== subLocKey.kt) {
|
|
1651
|
+
return false;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return true;
|
|
1655
|
+
}
|
|
1656
|
+
function findMatchingSubscriptions(event, subscriptions) {
|
|
1657
|
+
return subscriptions.filter(
|
|
1658
|
+
(subscription) => doesEventMatchSubscription(event, subscription)
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
function extractLocationValues(location) {
|
|
1662
|
+
return location.map((locKey) => String(locKey.lk));
|
|
1663
|
+
}
|
|
1664
|
+
function compareLocationValues(location1, location2) {
|
|
1665
|
+
if (location1.length !== location2.length) {
|
|
1666
|
+
return false;
|
|
1667
|
+
}
|
|
1668
|
+
return location1.every((locKey1, index) => {
|
|
1669
|
+
const locKey2 = location2[index];
|
|
1670
|
+
return locKey1.lk === locKey2.lk && locKey1.kt === locKey2.kt;
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// src/event/types.ts
|
|
1675
|
+
var STANDARD_EVENT_TYPES = {
|
|
1676
|
+
CREATE: "create",
|
|
1677
|
+
UPDATE: "update",
|
|
1678
|
+
DELETE: "delete",
|
|
1679
|
+
ACTION: "action"
|
|
1680
|
+
};
|
|
1681
|
+
var STANDARD_SCOPES = {
|
|
1682
|
+
FIRESTORE: "firestore",
|
|
1683
|
+
SEQUELIZE: "sequelize",
|
|
1684
|
+
POSTGRESQL: "postgresql",
|
|
1685
|
+
MYSQL: "mysql",
|
|
1686
|
+
MONGODB: "mongodb",
|
|
1687
|
+
REDIS: "redis"
|
|
1688
|
+
};
|
|
1689
|
+
var SubscriptionStatus = /* @__PURE__ */ ((SubscriptionStatus2) => {
|
|
1690
|
+
SubscriptionStatus2["PENDING"] = "pending";
|
|
1691
|
+
SubscriptionStatus2["ACTIVE"] = "active";
|
|
1692
|
+
SubscriptionStatus2["PAUSED"] = "paused";
|
|
1693
|
+
SubscriptionStatus2["ERROR"] = "error";
|
|
1694
|
+
SubscriptionStatus2["CANCELLED"] = "cancelled";
|
|
1695
|
+
return SubscriptionStatus2;
|
|
1696
|
+
})(SubscriptionStatus || {});
|
|
1697
|
+
var DEFAULT_EVENT_CONFIG = {
|
|
1698
|
+
maxBatchSize: 100,
|
|
1699
|
+
maxBatchWaitTime: 50,
|
|
1700
|
+
// 50ms
|
|
1701
|
+
maxRetryAttempts: 3,
|
|
1702
|
+
retryDelay: 1e3,
|
|
1703
|
+
// 1 second
|
|
1704
|
+
enableStats: true,
|
|
1705
|
+
maxSubscriptions: 1e3,
|
|
1706
|
+
subscriptionCleanupInterval: 3e5
|
|
1707
|
+
// 5 minutes
|
|
1708
|
+
};
|
|
1709
|
+
var EventSystemError = class extends Error {
|
|
1710
|
+
constructor(message, code, details) {
|
|
1711
|
+
super(message);
|
|
1712
|
+
this.code = code;
|
|
1713
|
+
this.details = details;
|
|
1714
|
+
this.name = "EventSystemError";
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
var SubscriptionError = class extends EventSystemError {
|
|
1718
|
+
constructor(message, subscriptionId, details) {
|
|
1719
|
+
super(message, "SUBSCRIPTION_ERROR", { subscriptionId, ...details });
|
|
1720
|
+
this.subscriptionId = subscriptionId;
|
|
1721
|
+
this.name = "SubscriptionError";
|
|
1722
|
+
}
|
|
1723
|
+
};
|
|
1724
|
+
var EventEmissionError = class extends EventSystemError {
|
|
1725
|
+
constructor(message, eventType, details) {
|
|
1726
|
+
super(message, "EVENT_EMISSION_ERROR", { eventType, ...details });
|
|
1727
|
+
this.eventType = eventType;
|
|
1728
|
+
this.name = "EventEmissionError";
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
var EventMatchingError = class extends EventSystemError {
|
|
1732
|
+
constructor(message, details) {
|
|
1733
|
+
super(message, "EVENT_MATCHING_ERROR", details);
|
|
1734
|
+
this.name = "EventMatchingError";
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
function createEventSystemError(type, message, details) {
|
|
1738
|
+
switch (type) {
|
|
1739
|
+
case "subscription":
|
|
1740
|
+
return new SubscriptionError(message, details?.subscriptionId || "unknown", details);
|
|
1741
|
+
case "emission":
|
|
1742
|
+
return new EventEmissionError(message, details?.eventType || "unknown", details);
|
|
1743
|
+
case "matching":
|
|
1744
|
+
return new EventMatchingError(message, details);
|
|
1745
|
+
case "general":
|
|
1746
|
+
default:
|
|
1747
|
+
return new EventSystemError(message, "GENERAL_ERROR", details);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
function isEventSystemError(error) {
|
|
1751
|
+
return error instanceof EventSystemError;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
// src/event/index.ts
|
|
1755
|
+
var EVENT_SYSTEM_VERSION = "1.0.0";
|
|
1756
|
+
var SUPPORTED_EVENT_TYPES = [
|
|
1757
|
+
"create",
|
|
1758
|
+
"update",
|
|
1759
|
+
"delete",
|
|
1760
|
+
"action"
|
|
1761
|
+
];
|
|
1762
|
+
var SUPPORTED_SCOPES = [
|
|
1763
|
+
"firestore",
|
|
1764
|
+
"sequelize",
|
|
1765
|
+
"postgresql",
|
|
1766
|
+
"mysql",
|
|
1767
|
+
"mongodb",
|
|
1768
|
+
"redis"
|
|
1769
|
+
];
|
|
1770
|
+
|
|
1530
1771
|
// src/operations/wrappers/createOneWrapper.ts
|
|
1531
1772
|
var logger9 = logger_default.get("operations", "wrappers", "one");
|
|
1532
1773
|
function createOneWrapper(coordinate, implementation, options = {}) {
|
|
@@ -2078,12 +2319,23 @@ export {
|
|
|
2078
2319
|
AItemService,
|
|
2079
2320
|
ActionError,
|
|
2080
2321
|
BusinessLogicError,
|
|
2322
|
+
DEFAULT_EVENT_CONFIG,
|
|
2081
2323
|
Dictionary,
|
|
2082
2324
|
DuplicateError,
|
|
2325
|
+
EVENT_SYSTEM_VERSION,
|
|
2326
|
+
EventEmissionError,
|
|
2327
|
+
EventMatchingError,
|
|
2328
|
+
EventSystemError,
|
|
2083
2329
|
IFactory,
|
|
2084
2330
|
IQFactory,
|
|
2085
2331
|
NotFoundError,
|
|
2086
2332
|
PermissionError,
|
|
2333
|
+
STANDARD_EVENT_TYPES,
|
|
2334
|
+
STANDARD_SCOPES,
|
|
2335
|
+
SUPPORTED_EVENT_TYPES,
|
|
2336
|
+
SUPPORTED_SCOPES,
|
|
2337
|
+
SubscriptionError,
|
|
2338
|
+
SubscriptionStatus,
|
|
2087
2339
|
ValidationError,
|
|
2088
2340
|
abbrevAgg,
|
|
2089
2341
|
abbrevCompoundCondition,
|
|
@@ -2093,6 +2345,7 @@ export {
|
|
|
2093
2345
|
abbrevQuery,
|
|
2094
2346
|
abbrevRef,
|
|
2095
2347
|
cPK,
|
|
2348
|
+
compareLocationValues,
|
|
2096
2349
|
constructPriKey,
|
|
2097
2350
|
createActionWrapper,
|
|
2098
2351
|
createAllActionWrapper,
|
|
@@ -2100,34 +2353,52 @@ export {
|
|
|
2100
2353
|
createAllWrapper,
|
|
2101
2354
|
createCoordinate,
|
|
2102
2355
|
createCreateWrapper,
|
|
2356
|
+
createEventSystemError,
|
|
2103
2357
|
createFacetWrapper,
|
|
2104
2358
|
createFindOneWrapper,
|
|
2105
2359
|
createFindWrapper,
|
|
2106
2360
|
createGetWrapper,
|
|
2361
|
+
createItemSubscription,
|
|
2362
|
+
createLocationSubscription,
|
|
2107
2363
|
createNormalizedHashFunction,
|
|
2108
2364
|
createOneWrapper,
|
|
2109
2365
|
createRemoveWrapper,
|
|
2110
2366
|
createUpdateWrapper,
|
|
2111
2367
|
createUpsertWrapper,
|
|
2368
|
+
doesEventMatchSubscription,
|
|
2369
|
+
doesEventTypeMatch,
|
|
2370
|
+
doesKeyMatch,
|
|
2371
|
+
doesKeyMatchLocation,
|
|
2372
|
+
doesLocationMatch,
|
|
2373
|
+
doesScopeMatch,
|
|
2112
2374
|
enhanceError,
|
|
2113
2375
|
executeWithContext,
|
|
2114
2376
|
executeWithContextSync,
|
|
2115
2377
|
extractKeyTypeArray,
|
|
2378
|
+
extractLocationValues,
|
|
2379
|
+
findMatchingSubscriptions,
|
|
2116
2380
|
generateKeyArray,
|
|
2381
|
+
generateSubscriptionId,
|
|
2117
2382
|
getErrorInfo,
|
|
2118
2383
|
ikToLKA,
|
|
2119
2384
|
isActionError,
|
|
2385
|
+
isActionEvent,
|
|
2120
2386
|
isComItem,
|
|
2121
2387
|
isComKey,
|
|
2122
2388
|
isComKeyEqual,
|
|
2123
2389
|
isComKeyEqualNormalized,
|
|
2124
2390
|
isCondition,
|
|
2391
|
+
isCreateEvent,
|
|
2392
|
+
isDeleteEvent,
|
|
2393
|
+
isEventSystemError,
|
|
2125
2394
|
isItemKey,
|
|
2126
2395
|
isItemKeyEqual,
|
|
2127
2396
|
isItemKeyEqualNormalized,
|
|
2397
|
+
isItemSubscription,
|
|
2128
2398
|
isLocKey,
|
|
2129
2399
|
isLocKeyEqual,
|
|
2130
2400
|
isLocKeyEqualNormalized,
|
|
2401
|
+
isLocationSubscription,
|
|
2131
2402
|
isComKey2 as isOperationComKey,
|
|
2132
2403
|
isPriKey2 as isOperationPriKey,
|
|
2133
2404
|
isPriItem,
|
|
@@ -2135,6 +2406,7 @@ export {
|
|
|
2135
2406
|
isPriKeyEqual,
|
|
2136
2407
|
isPriKeyEqualNormalized,
|
|
2137
2408
|
isQueryMatch,
|
|
2409
|
+
isUpdateEvent,
|
|
2138
2410
|
isValidComKey,
|
|
2139
2411
|
isValidItemKey,
|
|
2140
2412
|
isValidLocKey,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjell/core",
|
|
3
3
|
"description": "Core Item and Key Framework for Fjell",
|
|
4
|
-
"version": "4.4.
|
|
4
|
+
"version": "4.4.51",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"core",
|
|
7
7
|
"fjell"
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"docs:test": "cd docs && npm run test"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@fjell/logging": "^4.4.
|
|
44
|
+
"@fjell/logging": "^4.4.50",
|
|
45
45
|
"deepmerge": "^4.3.1",
|
|
46
46
|
"luxon": "^3.7.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@eslint/eslintrc": "^3.3.1",
|
|
50
50
|
"@eslint/js": "^9.32.0",
|
|
51
|
-
"@fjell/eslint-config": "^1.1.
|
|
51
|
+
"@fjell/eslint-config": "^1.1.28",
|
|
52
52
|
"@swc/core": "^1.13.2",
|
|
53
53
|
"@tsconfig/recommended": "^1.0.10",
|
|
54
54
|
"@types/luxon": "^3.6.2",
|
package/src/AItemService.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { AllItemTypeArrays } from "./keys";
|
|
2
|
-
|
|
3
|
-
export class AItemService<
|
|
4
|
-
S extends string,
|
|
5
|
-
L1 extends string,
|
|
6
|
-
L2 extends string = never,
|
|
7
|
-
L3 extends string = never,
|
|
8
|
-
L4 extends string = never,
|
|
9
|
-
L5 extends string = never
|
|
10
|
-
> {
|
|
11
|
-
|
|
12
|
-
private pkType: S;
|
|
13
|
-
private parentService: AItemService<L1, L2, L3, L4, L5, never> | null = null;
|
|
14
|
-
|
|
15
|
-
constructor(
|
|
16
|
-
pkType: S,
|
|
17
|
-
parentService?: AItemService<L1, L2, L3, L4, L5, never>,
|
|
18
|
-
) {
|
|
19
|
-
this.pkType = pkType;
|
|
20
|
-
if (parentService) {
|
|
21
|
-
this.parentService = parentService;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public getPkType = (): S => {
|
|
26
|
-
return this.pkType;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public getKeyTypes = (): AllItemTypeArrays<S, L1, L2, L3, L4, L5> => {
|
|
30
|
-
let keyTypes: readonly string[] = [this.getPkType()];
|
|
31
|
-
|
|
32
|
-
if (this.parentService) {
|
|
33
|
-
keyTypes = keyTypes.concat(this.parentService.getKeyTypes());
|
|
34
|
-
}
|
|
35
|
-
return keyTypes as AllItemTypeArrays<S, L1, L2, L3, L4, L5>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
}
|
package/src/Coordinate.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { ItemTypeArray } from "./keys";
|
|
2
|
-
import LibLogger from "./logger";
|
|
3
|
-
|
|
4
|
-
const logger = LibLogger.get("Coordinate");
|
|
5
|
-
|
|
6
|
-
export interface Coordinate<
|
|
7
|
-
S extends string,
|
|
8
|
-
L1 extends string = never,
|
|
9
|
-
L2 extends string = never,
|
|
10
|
-
L3 extends string = never,
|
|
11
|
-
L4 extends string = never,
|
|
12
|
-
L5 extends string = never,
|
|
13
|
-
> {
|
|
14
|
-
kta: ItemTypeArray<S, L1, L2, L3, L4, L5>;
|
|
15
|
-
scopes: string[];
|
|
16
|
-
toString: () => string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const createCoordinate = <
|
|
20
|
-
S extends string,
|
|
21
|
-
L1 extends string = never,
|
|
22
|
-
L2 extends string = never,
|
|
23
|
-
L3 extends string = never,
|
|
24
|
-
L4 extends string = never,
|
|
25
|
-
L5 extends string = never,
|
|
26
|
-
>(kta: ItemTypeArray<S, L1, L2, L3, L4, L5> | S, scopes: string[] = []): Coordinate<S, L1, L2, L3, L4, L5> => {
|
|
27
|
-
const ktArray = Array.isArray(kta) ? kta : [kta];
|
|
28
|
-
const toString = () => {
|
|
29
|
-
logger.debug("toString", { kta, scopes });
|
|
30
|
-
return `${ktArray.join(', ')} - ${scopes.join(', ')}`;
|
|
31
|
-
}
|
|
32
|
-
logger.debug("createCoordinate", { kta: ktArray, scopes, toString });
|
|
33
|
-
return { kta: ktArray as ItemTypeArray<S, L1, L2, L3, L4, L5>, scopes, toString };
|
|
34
|
-
}
|
|
35
|
-
|
package/src/dictionary.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import LibLogger from "./logger";
|
|
2
|
-
|
|
3
|
-
const logger = LibLogger.get("Dictionary");
|
|
4
|
-
|
|
5
|
-
interface DictionaryEntry<T, V> {
|
|
6
|
-
originalKey: T;
|
|
7
|
-
value: V;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export class Dictionary<T, V> {
|
|
11
|
-
protected map: { [key: string]: DictionaryEntry<T, V> } = {}
|
|
12
|
-
protected hashFunction = (key: T) => JSON.stringify(key);
|
|
13
|
-
|
|
14
|
-
constructor(map?: { [key: string]: V }, hashFunction?: (key: T) => string) {
|
|
15
|
-
if (hashFunction) {
|
|
16
|
-
this.hashFunction = hashFunction
|
|
17
|
-
}
|
|
18
|
-
if (map) {
|
|
19
|
-
// Convert legacy map format to new format
|
|
20
|
-
Object.entries(map).forEach(([hashedKey, value]) => {
|
|
21
|
-
try {
|
|
22
|
-
// Try to parse the key if it looks like JSON
|
|
23
|
-
const originalKey = JSON.parse(hashedKey) as T;
|
|
24
|
-
this.map[hashedKey] = { originalKey, value };
|
|
25
|
-
} catch {
|
|
26
|
-
// If parsing fails, we can't recover the original key
|
|
27
|
-
logger.warning('Cannot recover original key from legacy map entry', { hashedKey });
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public set(key: T, item: V): void {
|
|
34
|
-
logger.trace('set', { key, item });
|
|
35
|
-
const hashedKey = this.hashFunction(key);
|
|
36
|
-
this.map[hashedKey] = { originalKey: key, value: item };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
public get(key: T): V | null {
|
|
40
|
-
logger.trace('get', { key });
|
|
41
|
-
const hashedKey = this.hashFunction(key);
|
|
42
|
-
const entry = this.map[hashedKey];
|
|
43
|
-
// Check if entry exists AND the original key matches the requested key
|
|
44
|
-
return entry && this.keysEqual(entry.originalKey, key) ? entry.value : null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
private keysEqual(key1: T, key2: T): boolean {
|
|
48
|
-
// For basic equality check - this works for primitives and object references
|
|
49
|
-
// For deep equality, users can provide a custom hash function that avoids collisions
|
|
50
|
-
return key1 === key2;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public delete(key: T): void {
|
|
54
|
-
logger.trace('delete', { key });
|
|
55
|
-
const hashedKey = this.hashFunction(key);
|
|
56
|
-
delete this.map[hashedKey];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public keys(): T[] {
|
|
60
|
-
return Object.values(this.map).map(entry => entry.originalKey);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
public values(): V[] {
|
|
64
|
-
return Object.values(this.map).map(entry => entry.value);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
public includesKey(key: T): boolean {
|
|
68
|
-
const hashedKey = this.hashFunction(key);
|
|
69
|
-
const entry = this.map[hashedKey];
|
|
70
|
-
return entry ? this.keysEqual(entry.originalKey, key) : false;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
public clone(): Dictionary<T, V> {
|
|
74
|
-
const clonedMap: { [key: string]: V } = {};
|
|
75
|
-
Object.entries(this.map).forEach(([hashedKey, entry]) => {
|
|
76
|
-
clonedMap[hashedKey] = entry.value;
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const clone = new Dictionary<T, V>(clonedMap, this.hashFunction);
|
|
80
|
-
// Copy the entries directly to preserve original keys
|
|
81
|
-
clone.map = Object.assign({}, this.map);
|
|
82
|
-
return clone;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export interface ErrorInfo {
|
|
2
|
-
code: string;
|
|
3
|
-
message: string;
|
|
4
|
-
operation: {
|
|
5
|
-
type: 'get' | 'create' | 'update' | 'remove' | 'upsert' |
|
|
6
|
-
'all' | 'one' | 'find' | 'findOne' |
|
|
7
|
-
'action' | 'allAction' | 'facet' | 'allFacet';
|
|
8
|
-
name: string;
|
|
9
|
-
params: Record<string, any>;
|
|
10
|
-
};
|
|
11
|
-
context: {
|
|
12
|
-
itemType: string;
|
|
13
|
-
key?: {
|
|
14
|
-
primary?: string | number;
|
|
15
|
-
composite?: {
|
|
16
|
-
sk: string | number;
|
|
17
|
-
kta: string[];
|
|
18
|
-
locations?: Array<{ lk: string | number; kt: string }>;
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
affectedItems?: Array<{
|
|
22
|
-
id: string | number;
|
|
23
|
-
type: string;
|
|
24
|
-
displayName?: string;
|
|
25
|
-
}>;
|
|
26
|
-
parentLocation?: {
|
|
27
|
-
id: string | number;
|
|
28
|
-
type: string;
|
|
29
|
-
};
|
|
30
|
-
requiredPermission?: string;
|
|
31
|
-
};
|
|
32
|
-
details?: {
|
|
33
|
-
validOptions?: string[];
|
|
34
|
-
suggestedAction?: string;
|
|
35
|
-
retryable?: boolean;
|
|
36
|
-
conflictingValue?: any;
|
|
37
|
-
expectedValue?: any;
|
|
38
|
-
};
|
|
39
|
-
technical?: {
|
|
40
|
-
timestamp: string;
|
|
41
|
-
requestId?: string;
|
|
42
|
-
stackTrace?: string;
|
|
43
|
-
cause?: any;
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class ActionError extends Error {
|
|
48
|
-
constructor(
|
|
49
|
-
public readonly errorInfo: ErrorInfo,
|
|
50
|
-
cause?: Error
|
|
51
|
-
) {
|
|
52
|
-
super(errorInfo.message);
|
|
53
|
-
this.name = 'ActionError';
|
|
54
|
-
this.cause = cause;
|
|
55
|
-
|
|
56
|
-
// Ensure timestamp is always set
|
|
57
|
-
if (!this.errorInfo.technical) {
|
|
58
|
-
this.errorInfo.technical = { timestamp: new Date().toISOString() };
|
|
59
|
-
}
|
|
60
|
-
if (!this.errorInfo.technical.timestamp) {
|
|
61
|
-
this.errorInfo.technical.timestamp = new Date().toISOString();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
toJSON(): ErrorInfo {
|
|
66
|
-
return this.errorInfo;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { ActionError } from './ActionError';
|
|
2
|
-
|
|
3
|
-
export class BusinessLogicError extends ActionError {
|
|
4
|
-
constructor(
|
|
5
|
-
message: string,
|
|
6
|
-
suggestedAction?: string,
|
|
7
|
-
retryable: boolean = false
|
|
8
|
-
) {
|
|
9
|
-
super({
|
|
10
|
-
code: 'BUSINESS_LOGIC_ERROR',
|
|
11
|
-
message,
|
|
12
|
-
operation: { type: 'action', name: '', params: {} },
|
|
13
|
-
context: { itemType: '' },
|
|
14
|
-
details: {
|
|
15
|
-
suggestedAction,
|
|
16
|
-
retryable
|
|
17
|
-
},
|
|
18
|
-
technical: {
|
|
19
|
-
timestamp: new Date().toISOString()
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { ActionError } from './ActionError';
|
|
2
|
-
|
|
3
|
-
export class DuplicateError extends ActionError {
|
|
4
|
-
constructor(
|
|
5
|
-
message: string,
|
|
6
|
-
existingItemIdOrKey?: string | number | any,
|
|
7
|
-
duplicateField?: string
|
|
8
|
-
) {
|
|
9
|
-
// Extract ID and build key info
|
|
10
|
-
let existingItemId: string | number | null = null;
|
|
11
|
-
let keyInfo: any = null;
|
|
12
|
-
|
|
13
|
-
if (typeof existingItemIdOrKey === 'object' && existingItemIdOrKey !== null) {
|
|
14
|
-
// It's a key object - extract the primary key value and build proper key structure
|
|
15
|
-
existingItemId = existingItemIdOrKey.pk || existingItemIdOrKey.id || existingItemIdOrKey.primary || null;
|
|
16
|
-
|
|
17
|
-
// Build key info with primary field set
|
|
18
|
-
if (existingItemId !== null) {
|
|
19
|
-
keyInfo = {
|
|
20
|
-
primary: existingItemId,
|
|
21
|
-
...existingItemIdOrKey
|
|
22
|
-
};
|
|
23
|
-
} else {
|
|
24
|
-
keyInfo = existingItemIdOrKey;
|
|
25
|
-
}
|
|
26
|
-
} else if (typeof existingItemIdOrKey !== 'undefined') {
|
|
27
|
-
// It's a simple ID
|
|
28
|
-
existingItemId = existingItemIdOrKey;
|
|
29
|
-
keyInfo = { primary: existingItemId };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
super({
|
|
33
|
-
code: 'DUPLICATE_ERROR',
|
|
34
|
-
message,
|
|
35
|
-
operation: { type: 'create', name: '', params: {} },
|
|
36
|
-
context: {
|
|
37
|
-
itemType: '',
|
|
38
|
-
...(keyInfo && { key: keyInfo }),
|
|
39
|
-
...(existingItemId && {
|
|
40
|
-
affectedItems: [{
|
|
41
|
-
id: existingItemId,
|
|
42
|
-
type: '',
|
|
43
|
-
displayName: `Existing item with ${duplicateField || 'key'}`
|
|
44
|
-
}]
|
|
45
|
-
})
|
|
46
|
-
},
|
|
47
|
-
details: {
|
|
48
|
-
retryable: false,
|
|
49
|
-
conflictingValue: duplicateField
|
|
50
|
-
},
|
|
51
|
-
technical: {
|
|
52
|
-
timestamp: new Date().toISOString()
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { ActionError } from './ActionError';
|
|
2
|
-
|
|
3
|
-
export class NotFoundError extends ActionError {
|
|
4
|
-
constructor(
|
|
5
|
-
message: string,
|
|
6
|
-
itemType: string,
|
|
7
|
-
key?: any
|
|
8
|
-
) {
|
|
9
|
-
super({
|
|
10
|
-
code: 'NOT_FOUND',
|
|
11
|
-
message,
|
|
12
|
-
operation: { type: 'get', name: '', params: {} },
|
|
13
|
-
context: {
|
|
14
|
-
itemType,
|
|
15
|
-
key: typeof key === 'object' ? key : { primary: key }
|
|
16
|
-
},
|
|
17
|
-
details: { retryable: false },
|
|
18
|
-
technical: {
|
|
19
|
-
timestamp: new Date().toISOString()
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|