monorise 1.0.0 → 1.1.0-dev.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/base/index.d.ts +110 -3
- package/dist/base/index.js +3 -1
- package/dist/base/index.js.map +1 -1
- package/dist/cli/cli.js +6 -0
- package/dist/cli/cli.js.map +1 -1
- package/dist/core/chunk-QV4Q5377.js +76 -0
- package/dist/core/chunk-QV4Q5377.js.map +1 -0
- package/dist/core/index.d.ts +191 -38
- package/dist/core/index.js +1539 -122
- package/dist/core/index.js.map +1 -1
- package/dist/core/service.config-ZJEZ6EKA.js +13 -0
- package/dist/core/service.config-ZJEZ6EKA.js.map +1 -0
- package/dist/proxy/index.d.ts +35 -0
- package/dist/proxy/index.js +75 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/react/actions/websocket.action.d.ts +71 -0
- package/dist/react/actions/websocket.action.d.ts.map +1 -0
- package/dist/react/chunk-2QOYO3GF.js +182 -0
- package/dist/react/chunk-2QOYO3GF.js.map +1 -0
- package/dist/react/chunk-4WSYM746.js +383 -0
- package/dist/react/chunk-4WSYM746.js.map +1 -0
- package/dist/react/chunk-5XIRNUBL.js +43 -0
- package/dist/react/chunk-5XIRNUBL.js.map +1 -0
- package/dist/react/chunk-7JDOKZGQ.js +172 -0
- package/dist/react/chunk-7JDOKZGQ.js.map +1 -0
- package/dist/react/chunk-A5TI2FW3.js +13 -0
- package/dist/react/chunk-A5TI2FW3.js.map +1 -0
- package/dist/react/chunk-BJXCFDMF.js +15 -0
- package/dist/react/chunk-BJXCFDMF.js.map +1 -0
- package/dist/react/chunk-BUTF5RJU.js +29 -0
- package/dist/react/chunk-BUTF5RJU.js.map +1 -0
- package/dist/react/chunk-DTRWUIDH.js +402 -0
- package/dist/react/chunk-DTRWUIDH.js.map +1 -0
- package/dist/react/chunk-GFVCNWVT.js +54 -0
- package/dist/react/chunk-GFVCNWVT.js.map +1 -0
- package/dist/react/chunk-JT5EZZSL.js +489 -0
- package/dist/react/chunk-JT5EZZSL.js.map +1 -0
- package/dist/react/chunk-KJX5LOMN.js +43 -0
- package/dist/react/chunk-KJX5LOMN.js.map +1 -0
- package/dist/react/chunk-KLXK4V6G.js +65 -0
- package/dist/react/chunk-KLXK4V6G.js.map +1 -0
- package/dist/react/chunk-LJLMKEKI.js +245 -0
- package/dist/react/chunk-LJLMKEKI.js.map +1 -0
- package/dist/react/chunk-MIXAYX55.js +147 -0
- package/dist/react/chunk-MIXAYX55.js.map +1 -0
- package/dist/react/chunk-RPNCWADG.js +248 -0
- package/dist/react/chunk-RPNCWADG.js.map +1 -0
- package/dist/react/chunk-S6RDMHHH.js +47 -0
- package/dist/react/chunk-S6RDMHHH.js.map +1 -0
- package/dist/react/chunk-U6RIOMF4.js +893 -0
- package/dist/react/chunk-U6RIOMF4.js.map +1 -0
- package/dist/react/chunk-WCRLJFBW.js +5568 -0
- package/dist/react/chunk-WCRLJFBW.js.map +1 -0
- package/dist/react/chunk-YF6S7S36.js +588 -0
- package/dist/react/chunk-YF6S7S36.js.map +1 -0
- package/dist/react/dist-es-5WYA7CWK.js +379 -0
- package/dist/react/dist-es-5WYA7CWK.js.map +1 -0
- package/dist/react/dist-es-CR5AOOCO.js +333 -0
- package/dist/react/dist-es-CR5AOOCO.js.map +1 -0
- package/dist/react/dist-es-KZ3GLAJI.js +494 -0
- package/dist/react/dist-es-KZ3GLAJI.js.map +1 -0
- package/dist/react/dist-es-R4TRTT45.js +22 -0
- package/dist/react/dist-es-R4TRTT45.js.map +1 -0
- package/dist/react/dist-es-SKDPAJEW.js +169 -0
- package/dist/react/dist-es-SKDPAJEW.js.map +1 -0
- package/dist/react/dist-es-TOHBZNTZ.js +71 -0
- package/dist/react/dist-es-TOHBZNTZ.js.map +1 -0
- package/dist/react/dist-es-XNAC47MK.js +90 -0
- package/dist/react/dist-es-XNAC47MK.js.map +1 -0
- package/dist/react/event-streams-WAZW4P3K.js +277 -0
- package/dist/react/event-streams-WAZW4P3K.js.map +1 -0
- package/dist/react/index.d.ts +53 -4
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +10991 -190
- package/dist/react/index.js.map +1 -1
- package/dist/react/loadSso-KXVD6CBM.js +556 -0
- package/dist/react/loadSso-KXVD6CBM.js.map +1 -0
- package/dist/react/service.config-I7RKP6FE.js +14 -0
- package/dist/react/service.config-I7RKP6FE.js.map +1 -0
- package/dist/react/services/core.service.d.ts +11 -1
- package/dist/react/services/core.service.d.ts.map +1 -1
- package/dist/react/signin-SEY3FDQ5.js +653 -0
- package/dist/react/signin-SEY3FDQ5.js.map +1 -0
- package/dist/react/sso-oidc-REODVHH5.js +786 -0
- package/dist/react/sso-oidc-REODVHH5.js.map +1 -0
- package/dist/react/sts-I3M4QP37.js +3948 -0
- package/dist/react/sts-I3M4QP37.js.map +1 -0
- package/dist/react/websocket/WebSocketManager.d.ts +68 -0
- package/dist/react/websocket/WebSocketManager.d.ts.map +1 -0
- package/dist/react/websocket/index.d.ts +3 -0
- package/dist/react/websocket/index.d.ts.map +1 -0
- package/dist/react/websocket/optimistic.d.ts +51 -0
- package/dist/react/websocket/optimistic.d.ts.map +1 -0
- package/dist/react/websocket-OSLLJSNO.js +10 -0
- package/dist/react/websocket-OSLLJSNO.js.map +1 -0
- package/dist/sst/components/monorise-core.d.ts +10 -0
- package/dist/sst/components/monorise-core.d.ts.map +1 -1
- package/dist/sst/index.js +75 -12
- package/dist/sst/index.js.map +1 -1
- package/package.json +9 -1
package/dist/core/index.js
CHANGED
|
@@ -1,58 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (__getOwnPropSymbols)
|
|
13
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
-
if (__propIsEnum.call(b, prop))
|
|
15
|
-
__defNormalProp(a, prop, b[prop]);
|
|
16
|
-
}
|
|
17
|
-
return a;
|
|
18
|
-
};
|
|
19
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
-
var __objRest = (source, exclude) => {
|
|
21
|
-
var target = {};
|
|
22
|
-
for (var prop in source)
|
|
23
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
24
|
-
target[prop] = source[prop];
|
|
25
|
-
if (source != null && __getOwnPropSymbols)
|
|
26
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
27
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
28
|
-
target[prop] = source[prop];
|
|
29
|
-
}
|
|
30
|
-
return target;
|
|
31
|
-
};
|
|
32
|
-
var __export = (target, all) => {
|
|
33
|
-
for (var name in all)
|
|
34
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
35
|
-
};
|
|
36
|
-
var __async = (__this, __arguments, generator) => {
|
|
37
|
-
return new Promise((resolve, reject) => {
|
|
38
|
-
var fulfilled = (value) => {
|
|
39
|
-
try {
|
|
40
|
-
step(generator.next(value));
|
|
41
|
-
} catch (e) {
|
|
42
|
-
reject(e);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
var rejected = (value) => {
|
|
46
|
-
try {
|
|
47
|
-
step(generator.throw(value));
|
|
48
|
-
} catch (e) {
|
|
49
|
-
reject(e);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
53
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
54
|
-
});
|
|
55
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
CORE_EVENT_BUS,
|
|
3
|
+
CORE_TABLE,
|
|
4
|
+
ENTITY_REPLICATION_INDEX,
|
|
5
|
+
MUTUAL_REPLICATION_INDEX,
|
|
6
|
+
__async,
|
|
7
|
+
__export,
|
|
8
|
+
__objRest,
|
|
9
|
+
__spreadProps,
|
|
10
|
+
__spreadValues
|
|
11
|
+
} from "./chunk-QV4Q5377.js";
|
|
56
12
|
|
|
57
13
|
// controllers/setupRoutes.ts
|
|
58
14
|
import { Hono } from "hono";
|
|
@@ -140,6 +96,11 @@ var setupCommonRoutes = (container) => {
|
|
|
140
96
|
container.deleteEntityController.controller
|
|
141
97
|
);
|
|
142
98
|
app.get("/tag/:entityType/:tagName", container.listTagsController.controller);
|
|
99
|
+
app.post("/transaction", container.executeTransactionController.controller);
|
|
100
|
+
app.post(
|
|
101
|
+
"/ws/ticket/:entityType/:entityId",
|
|
102
|
+
container.createTicketController.controller
|
|
103
|
+
);
|
|
143
104
|
return app;
|
|
144
105
|
};
|
|
145
106
|
|
|
@@ -175,6 +136,7 @@ var StandardErrorCode = {
|
|
|
175
136
|
ENTITY_ID_IS_UNDEFINED: "ENTITY_ID_IS_UNDEFINED",
|
|
176
137
|
ENTITY_IS_UNDEFINED: "ENTITY_IS_UNDEFINED",
|
|
177
138
|
ENTITY_NOT_FOUND: "ENTITY_NOT_FOUND",
|
|
139
|
+
INVALID_CONDITION: "INVALID_CONDITION",
|
|
178
140
|
INVALID_ENTITY_TYPE: "INVALID_ENTITY_TYPE",
|
|
179
141
|
INVALID_MUTUAL: "INVALID_MUTUAL",
|
|
180
142
|
INVALID_QUERY: "INVALID_QUERY",
|
|
@@ -187,7 +149,10 @@ var StandardErrorCode = {
|
|
|
187
149
|
REPLICATION_ERROR: "REPLICATION_ERROR",
|
|
188
150
|
RETRYABLE_MUTUAL_LOCK_CONFLICT: "RETRYABLE_MUTUAL_LOCK_CONFLICT",
|
|
189
151
|
TAG_IS_UNDEFINED: "TAG_IS_UNDEFINED",
|
|
152
|
+
TRANSACTION_EMPTY: "TRANSACTION_EMPTY",
|
|
190
153
|
TRANSACTION_FAILED: "TRANSACTION_FAILED",
|
|
154
|
+
TRANSACTION_ITEM_LIMIT_EXCEEDED: "TRANSACTION_ITEM_LIMIT_EXCEEDED",
|
|
155
|
+
TRANSACTION_UNIQUE_FIELD_UPDATE: "TRANSACTION_UNIQUE_FIELD_UPDATE",
|
|
191
156
|
UNIQUE_VALUE_EXISTS: "UNIQUE_VALUE_EXISTS"
|
|
192
157
|
};
|
|
193
158
|
|
|
@@ -832,22 +797,22 @@ var EntityRepository = class extends Repository {
|
|
|
832
797
|
}
|
|
833
798
|
});
|
|
834
799
|
}
|
|
835
|
-
adjustEntity(entityType, entityId, adjustments,
|
|
800
|
+
adjustEntity(entityType, entityId, adjustments, opts) {
|
|
836
801
|
return __async(this, null, function* () {
|
|
837
802
|
const entity = new Entity(entityType, entityId);
|
|
838
|
-
const { UpdateExpression,
|
|
803
|
+
const { UpdateExpression, ExpressionAttributeNames, ExpressionAttributeValues } = this.toAdjustUpdate(adjustments);
|
|
839
804
|
const updatedAtExpression = ", #updatedAt = :updatedAt";
|
|
840
805
|
ExpressionAttributeNames["#updatedAt"] = "updatedAt";
|
|
841
806
|
ExpressionAttributeValues[":updatedAt"] = { S: (/* @__PURE__ */ new Date()).toISOString() };
|
|
842
|
-
const resp = yield this.dynamodbClient.updateItem(
|
|
807
|
+
const resp = yield this.dynamodbClient.updateItem({
|
|
843
808
|
TableName: this.TABLE_NAME,
|
|
844
809
|
Key: entity.keys(),
|
|
845
|
-
UpdateExpression: UpdateExpression + updatedAtExpression
|
|
846
|
-
|
|
847
|
-
ExpressionAttributeNames,
|
|
848
|
-
ExpressionAttributeValues,
|
|
810
|
+
UpdateExpression: UpdateExpression + updatedAtExpression,
|
|
811
|
+
ConditionExpression: (opts == null ? void 0 : opts.ConditionExpression) || "attribute_exists(PK)",
|
|
812
|
+
ExpressionAttributeNames: __spreadValues(__spreadValues({}, ExpressionAttributeNames), opts == null ? void 0 : opts.ExpressionAttributeNames),
|
|
813
|
+
ExpressionAttributeValues: __spreadValues(__spreadValues({}, ExpressionAttributeValues), opts == null ? void 0 : opts.ExpressionAttributeValues),
|
|
849
814
|
ReturnValues: "ALL_NEW"
|
|
850
|
-
})
|
|
815
|
+
});
|
|
851
816
|
return Entity.fromItem(resp.Attributes);
|
|
852
817
|
});
|
|
853
818
|
}
|
|
@@ -1857,6 +1822,217 @@ var TagRepository = class extends Repository {
|
|
|
1857
1822
|
}
|
|
1858
1823
|
};
|
|
1859
1824
|
|
|
1825
|
+
// data/WebSocket.ts
|
|
1826
|
+
import {
|
|
1827
|
+
DeleteCommand,
|
|
1828
|
+
DynamoDBDocumentClient,
|
|
1829
|
+
PutCommand,
|
|
1830
|
+
QueryCommand
|
|
1831
|
+
} from "@aws-sdk/lib-dynamodb";
|
|
1832
|
+
var WebSocketRepository = class extends Repository {
|
|
1833
|
+
constructor(tableName, dynamodbClient) {
|
|
1834
|
+
super();
|
|
1835
|
+
this.tableName = tableName;
|
|
1836
|
+
this.dynamodbClient = dynamodbClient;
|
|
1837
|
+
this.docClient = DynamoDBDocumentClient.from(dynamodbClient);
|
|
1838
|
+
}
|
|
1839
|
+
createConnection(connectionId, metadata, expiresAt) {
|
|
1840
|
+
return __async(this, null, function* () {
|
|
1841
|
+
yield this.docClient.send(
|
|
1842
|
+
new PutCommand({
|
|
1843
|
+
TableName: this.tableName,
|
|
1844
|
+
Item: __spreadProps(__spreadValues({
|
|
1845
|
+
PK: `CONN#${connectionId}`,
|
|
1846
|
+
SK: "#METADATA#",
|
|
1847
|
+
connectionId
|
|
1848
|
+
}, metadata), {
|
|
1849
|
+
expiresAt
|
|
1850
|
+
})
|
|
1851
|
+
})
|
|
1852
|
+
);
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
getConnection(connectionId) {
|
|
1856
|
+
return __async(this, null, function* () {
|
|
1857
|
+
var _a;
|
|
1858
|
+
const result = yield this.docClient.send(
|
|
1859
|
+
new QueryCommand({
|
|
1860
|
+
TableName: this.tableName,
|
|
1861
|
+
KeyConditionExpression: "PK = :pk",
|
|
1862
|
+
ExpressionAttributeValues: {
|
|
1863
|
+
":pk": `CONN#${connectionId}`
|
|
1864
|
+
}
|
|
1865
|
+
})
|
|
1866
|
+
);
|
|
1867
|
+
return (_a = result.Items) == null ? void 0 : _a[0];
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
deleteConnection(connectionId) {
|
|
1871
|
+
return __async(this, null, function* () {
|
|
1872
|
+
yield this.docClient.send(
|
|
1873
|
+
new DeleteCommand({
|
|
1874
|
+
TableName: this.tableName,
|
|
1875
|
+
Key: {
|
|
1876
|
+
PK: `CONN#${connectionId}`,
|
|
1877
|
+
SK: "#METADATA#"
|
|
1878
|
+
}
|
|
1879
|
+
})
|
|
1880
|
+
);
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
createSubscription(subKey, connectionId, data) {
|
|
1884
|
+
return __async(this, null, function* () {
|
|
1885
|
+
yield this.docClient.send(
|
|
1886
|
+
new PutCommand({
|
|
1887
|
+
TableName: this.tableName,
|
|
1888
|
+
Item: __spreadValues({
|
|
1889
|
+
PK: subKey,
|
|
1890
|
+
SK: `CONN#${connectionId}`,
|
|
1891
|
+
R1PK: `CONN#${connectionId}`,
|
|
1892
|
+
R1SK: subKey,
|
|
1893
|
+
connectionId
|
|
1894
|
+
}, data)
|
|
1895
|
+
})
|
|
1896
|
+
);
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
deleteSubscription(subKey, connectionId) {
|
|
1900
|
+
return __async(this, null, function* () {
|
|
1901
|
+
yield this.docClient.send(
|
|
1902
|
+
new DeleteCommand({
|
|
1903
|
+
TableName: this.tableName,
|
|
1904
|
+
Key: {
|
|
1905
|
+
PK: subKey,
|
|
1906
|
+
SK: `CONN#${connectionId}`
|
|
1907
|
+
}
|
|
1908
|
+
})
|
|
1909
|
+
);
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
querySubscriptionsByKey(subKey) {
|
|
1913
|
+
return __async(this, null, function* () {
|
|
1914
|
+
const result = yield this.docClient.send(
|
|
1915
|
+
new QueryCommand({
|
|
1916
|
+
TableName: this.tableName,
|
|
1917
|
+
KeyConditionExpression: "PK = :pk",
|
|
1918
|
+
ExpressionAttributeValues: {
|
|
1919
|
+
":pk": subKey
|
|
1920
|
+
},
|
|
1921
|
+
ConsistentRead: true
|
|
1922
|
+
})
|
|
1923
|
+
);
|
|
1924
|
+
return result.Items || [];
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
querySubscriptionsByConnectionId(connectionId) {
|
|
1928
|
+
return __async(this, null, function* () {
|
|
1929
|
+
const { ENTITY_REPLICATION_INDEX: ENTITY_REPLICATION_INDEX2 } = yield import("./service.config-ZJEZ6EKA.js");
|
|
1930
|
+
const result = yield this.docClient.send(
|
|
1931
|
+
new QueryCommand({
|
|
1932
|
+
TableName: this.tableName,
|
|
1933
|
+
IndexName: ENTITY_REPLICATION_INDEX2,
|
|
1934
|
+
KeyConditionExpression: "R1PK = :r1pk",
|
|
1935
|
+
ExpressionAttributeValues: {
|
|
1936
|
+
":r1pk": `CONN#${connectionId}`
|
|
1937
|
+
}
|
|
1938
|
+
})
|
|
1939
|
+
);
|
|
1940
|
+
return result.Items || [];
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
createTicket(ticket, entityType, entityId, feedTypes, expiresAt) {
|
|
1944
|
+
return __async(this, null, function* () {
|
|
1945
|
+
yield this.docClient.send(
|
|
1946
|
+
new PutCommand({
|
|
1947
|
+
TableName: this.tableName,
|
|
1948
|
+
Item: {
|
|
1949
|
+
PK: `TICKET#${ticket}`,
|
|
1950
|
+
SK: "#METADATA#",
|
|
1951
|
+
entityType,
|
|
1952
|
+
entityId,
|
|
1953
|
+
feedTypes,
|
|
1954
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1955
|
+
expiresAt
|
|
1956
|
+
}
|
|
1957
|
+
})
|
|
1958
|
+
);
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
consumeTicket(ticket) {
|
|
1962
|
+
return __async(this, null, function* () {
|
|
1963
|
+
try {
|
|
1964
|
+
const result = yield this.docClient.send(
|
|
1965
|
+
new DeleteCommand({
|
|
1966
|
+
TableName: this.tableName,
|
|
1967
|
+
Key: {
|
|
1968
|
+
PK: `TICKET#${ticket}`,
|
|
1969
|
+
SK: "#METADATA#"
|
|
1970
|
+
},
|
|
1971
|
+
ConditionExpression: "attribute_exists(PK)",
|
|
1972
|
+
ReturnValues: "ALL_OLD"
|
|
1973
|
+
})
|
|
1974
|
+
);
|
|
1975
|
+
const item = result.Attributes;
|
|
1976
|
+
if (!item) return null;
|
|
1977
|
+
const expiresAt = item.expiresAt;
|
|
1978
|
+
if (expiresAt && expiresAt < Math.floor(Date.now() / 1e3)) {
|
|
1979
|
+
return null;
|
|
1980
|
+
}
|
|
1981
|
+
return {
|
|
1982
|
+
entityType: item.entityType,
|
|
1983
|
+
entityId: item.entityId,
|
|
1984
|
+
feedTypes: item.feedTypes || []
|
|
1985
|
+
};
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
if (error instanceof Error && error.name === "ConditionalCheckFailedException") {
|
|
1988
|
+
return null;
|
|
1989
|
+
}
|
|
1990
|
+
throw error;
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
queryMutualConnections(byEntityType, byEntityId) {
|
|
1995
|
+
return __async(this, null, function* () {
|
|
1996
|
+
const result = yield this.docClient.send(
|
|
1997
|
+
new QueryCommand({
|
|
1998
|
+
TableName: this.tableName,
|
|
1999
|
+
KeyConditionExpression: "PK = :pk",
|
|
2000
|
+
ExpressionAttributeValues: {
|
|
2001
|
+
":pk": `${byEntityType}#${byEntityId}`
|
|
2002
|
+
},
|
|
2003
|
+
ProjectionExpression: "SK",
|
|
2004
|
+
ConsistentRead: true
|
|
2005
|
+
})
|
|
2006
|
+
);
|
|
2007
|
+
const connections = [];
|
|
2008
|
+
for (const item of result.Items || []) {
|
|
2009
|
+
const sk = item.SK;
|
|
2010
|
+
if (!sk || sk === "#METADATA#" || sk.startsWith("#")) continue;
|
|
2011
|
+
const parts = sk.split("#");
|
|
2012
|
+
if (parts.length >= 2) {
|
|
2013
|
+
connections.push({ entityType: parts[0], entityId: parts[1] });
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
return connections;
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
2019
|
+
queryFeedSubscriptions(entityType, entityId) {
|
|
2020
|
+
return __async(this, null, function* () {
|
|
2021
|
+
const result = yield this.docClient.send(
|
|
2022
|
+
new QueryCommand({
|
|
2023
|
+
TableName: this.tableName,
|
|
2024
|
+
KeyConditionExpression: "PK = :pk",
|
|
2025
|
+
ExpressionAttributeValues: {
|
|
2026
|
+
":pk": `SUB#FEED#${entityType}#${entityId}`
|
|
2027
|
+
},
|
|
2028
|
+
ConsistentRead: true
|
|
2029
|
+
})
|
|
2030
|
+
);
|
|
2031
|
+
return result.Items || [];
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
|
|
1860
2036
|
// handles/app.ts
|
|
1861
2037
|
import { Hono as Hono2 } from "hono";
|
|
1862
2038
|
import { handle } from "hono/aws-lambda";
|
|
@@ -1896,14 +2072,6 @@ import {
|
|
|
1896
2072
|
PutEventsCommand
|
|
1897
2073
|
} from "@aws-sdk/client-eventbridge";
|
|
1898
2074
|
|
|
1899
|
-
// constants/table.ts
|
|
1900
|
-
var ENTITY_REPLICATION_INDEX = "ENTITY_REPLICATION_INDEX";
|
|
1901
|
-
var MUTUAL_REPLICATION_INDEX = "MUTUAL_REPLICATION_INDEX";
|
|
1902
|
-
|
|
1903
|
-
// configs/service.config.ts
|
|
1904
|
-
var CORE_TABLE = process.env.CORE_TABLE || "";
|
|
1905
|
-
var CORE_EVENT_BUS = process.env.CORE_EVENT_BUS || "";
|
|
1906
|
-
|
|
1907
2075
|
// types/event.ts
|
|
1908
2076
|
var SOURCE = {
|
|
1909
2077
|
CORE: "core-service",
|
|
@@ -2147,7 +2315,7 @@ var handler2 = (container) => (ev) => __async(null, null, function* () {
|
|
|
2147
2315
|
const { entityRepository, mutualRepository, publishEvent: publishEvent2 } = container;
|
|
2148
2316
|
yield Promise.allSettled(
|
|
2149
2317
|
ev.Records.map((record) => __async(null, null, function* () {
|
|
2150
|
-
var _a, _b, _c, _d;
|
|
2318
|
+
var _a, _b, _c, _d, _e;
|
|
2151
2319
|
const errorContext = {};
|
|
2152
2320
|
const body = parseSQSBusEvent(record.body);
|
|
2153
2321
|
const { detail } = body;
|
|
@@ -2170,6 +2338,7 @@ var handler2 = (container) => (ev) => __async(null, null, function* () {
|
|
|
2170
2338
|
);
|
|
2171
2339
|
}
|
|
2172
2340
|
const mutualDataProcessor = (_d = config.mutualDataProcessor) != null ? _d : (() => ({}));
|
|
2341
|
+
const mutualDataSchema = (_e = config.mutual) == null ? void 0 : _e.mutualDataSchema;
|
|
2173
2342
|
yield mutualRepository.createMutualLock({
|
|
2174
2343
|
byEntityType,
|
|
2175
2344
|
byEntityId,
|
|
@@ -2205,6 +2374,20 @@ var handler2 = (container) => (ev) => __async(null, null, function* () {
|
|
|
2205
2374
|
addedEntityIds,
|
|
2206
2375
|
(id) => __async(null, null, function* () {
|
|
2207
2376
|
const entity = yield entityRepository.getEntity(entityType, id);
|
|
2377
|
+
const processedMutualData = mutualDataProcessor(
|
|
2378
|
+
mutualIds,
|
|
2379
|
+
new Mutual(
|
|
2380
|
+
byEntityType,
|
|
2381
|
+
byEntityId,
|
|
2382
|
+
byEntity.data,
|
|
2383
|
+
entityType,
|
|
2384
|
+
id,
|
|
2385
|
+
entity.data,
|
|
2386
|
+
{}
|
|
2387
|
+
),
|
|
2388
|
+
customContext
|
|
2389
|
+
);
|
|
2390
|
+
const parsedMutualData = mutualDataSchema ? mutualDataSchema.parse(processedMutualData) : processedMutualData;
|
|
2208
2391
|
yield mutualRepository.createMutual(
|
|
2209
2392
|
byEntityType,
|
|
2210
2393
|
byEntityId,
|
|
@@ -2212,19 +2395,7 @@ var handler2 = (container) => (ev) => __async(null, null, function* () {
|
|
|
2212
2395
|
entityType,
|
|
2213
2396
|
id,
|
|
2214
2397
|
entity.data,
|
|
2215
|
-
|
|
2216
|
-
mutualIds,
|
|
2217
|
-
new Mutual(
|
|
2218
|
-
byEntityType,
|
|
2219
|
-
byEntityId,
|
|
2220
|
-
byEntity.data,
|
|
2221
|
-
entityType,
|
|
2222
|
-
id,
|
|
2223
|
-
entity.data,
|
|
2224
|
-
{}
|
|
2225
|
-
),
|
|
2226
|
-
customContext
|
|
2227
|
-
),
|
|
2398
|
+
parsedMutualData,
|
|
2228
2399
|
{
|
|
2229
2400
|
ConditionExpression: "attribute_not_exists(#mutualUpdatedAt) OR #mutualUpdatedAt < :publishedAt",
|
|
2230
2401
|
ExpressionAttributeNames: {
|
|
@@ -2261,25 +2432,27 @@ var handler2 = (container) => (ev) => __async(null, null, function* () {
|
|
|
2261
2432
|
const updateEntities = yield processEntities(
|
|
2262
2433
|
toUpdateEntityIds,
|
|
2263
2434
|
(id) => __async(null, null, function* () {
|
|
2435
|
+
const processedMutualData = mutualDataProcessor(
|
|
2436
|
+
mutualIds,
|
|
2437
|
+
new Mutual(
|
|
2438
|
+
byEntityType,
|
|
2439
|
+
byEntityId,
|
|
2440
|
+
byEntity.data,
|
|
2441
|
+
entityType,
|
|
2442
|
+
id,
|
|
2443
|
+
{},
|
|
2444
|
+
{}
|
|
2445
|
+
),
|
|
2446
|
+
customContext
|
|
2447
|
+
);
|
|
2448
|
+
const parsedMutualData = mutualDataSchema ? mutualDataSchema.parse(processedMutualData) : processedMutualData;
|
|
2264
2449
|
yield mutualRepository.updateMutual(
|
|
2265
2450
|
byEntityType,
|
|
2266
2451
|
byEntityId,
|
|
2267
2452
|
entityType,
|
|
2268
2453
|
id,
|
|
2269
2454
|
{
|
|
2270
|
-
mutualData:
|
|
2271
|
-
mutualIds,
|
|
2272
|
-
new Mutual(
|
|
2273
|
-
byEntityType,
|
|
2274
|
-
byEntityId,
|
|
2275
|
-
byEntity.data,
|
|
2276
|
-
entityType,
|
|
2277
|
-
id,
|
|
2278
|
-
{},
|
|
2279
|
-
{}
|
|
2280
|
-
),
|
|
2281
|
-
customContext
|
|
2282
|
-
),
|
|
2455
|
+
mutualData: parsedMutualData,
|
|
2283
2456
|
mutualUpdatedAt: publishedAt
|
|
2284
2457
|
},
|
|
2285
2458
|
{
|
|
@@ -2855,6 +3028,458 @@ var handler5 = (container) => (ev) => __async(null, null, function* () {
|
|
|
2855
3028
|
return { batchItemFailures };
|
|
2856
3029
|
});
|
|
2857
3030
|
|
|
3031
|
+
// processors/websocket-processor.ts
|
|
3032
|
+
import {
|
|
3033
|
+
ApiGatewayManagementApiClient,
|
|
3034
|
+
PostToConnectionCommand
|
|
3035
|
+
} from "@aws-sdk/client-apigatewaymanagementapi";
|
|
3036
|
+
import { unmarshall as unmarshall4 } from "@aws-sdk/util-dynamodb";
|
|
3037
|
+
import { ulid as ulid3 } from "ulid";
|
|
3038
|
+
var SUB_ENTITY_TYPE = "SUB#ENTITY#";
|
|
3039
|
+
var SUB_MUTUAL_TYPE = "SUB#MUTUAL#";
|
|
3040
|
+
var SUB_EPHEMERAL = "SUB#EPHEMERAL#";
|
|
3041
|
+
var SUB_FEED = "SUB#FEED#";
|
|
3042
|
+
var getWsEndpoint = () => process.env.WEBSOCKET_MANAGEMENT_ENDPOINT || "";
|
|
3043
|
+
var connect = (container) => (event) => __async(null, null, function* () {
|
|
3044
|
+
var _a, _b, _c, _d;
|
|
3045
|
+
const connectionId = event.requestContext.connectionId;
|
|
3046
|
+
if (!connectionId) {
|
|
3047
|
+
return { statusCode: 400, body: "Missing connection ID" };
|
|
3048
|
+
}
|
|
3049
|
+
const expiresAt = Math.floor(Date.now() / 1e3) + 2 * 60 * 60;
|
|
3050
|
+
const ticket = (_a = event.queryStringParameters) == null ? void 0 : _a.ticket;
|
|
3051
|
+
const token = ((_b = event.queryStringParameters) == null ? void 0 : _b.token) || ((_c = event.headers) == null ? void 0 : _c.authorization) || ((_d = event.headers) == null ? void 0 : _d.Authorization);
|
|
3052
|
+
if (!ticket && !token) {
|
|
3053
|
+
return { statusCode: 401, body: "Unauthorized" };
|
|
3054
|
+
}
|
|
3055
|
+
const wsRepo = container.websocketRepository;
|
|
3056
|
+
try {
|
|
3057
|
+
let entityType;
|
|
3058
|
+
let entityId;
|
|
3059
|
+
let feedTypes;
|
|
3060
|
+
if (ticket) {
|
|
3061
|
+
const ticketData = yield wsRepo.consumeTicket(ticket);
|
|
3062
|
+
if (!ticketData) {
|
|
3063
|
+
return { statusCode: 401, body: "Invalid or expired ticket" };
|
|
3064
|
+
}
|
|
3065
|
+
entityType = ticketData.entityType;
|
|
3066
|
+
entityId = ticketData.entityId;
|
|
3067
|
+
feedTypes = ticketData.feedTypes;
|
|
3068
|
+
} else {
|
|
3069
|
+
entityId = token;
|
|
3070
|
+
}
|
|
3071
|
+
yield wsRepo.createConnection(
|
|
3072
|
+
connectionId,
|
|
3073
|
+
__spreadProps(__spreadValues(__spreadValues({}, entityType && { entityType }), entityId && { entityId }), {
|
|
3074
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3075
|
+
}),
|
|
3076
|
+
expiresAt
|
|
3077
|
+
);
|
|
3078
|
+
if (entityType && entityId && feedTypes) {
|
|
3079
|
+
yield wsRepo.createSubscription(
|
|
3080
|
+
`${SUB_FEED}${entityType}#${entityId}`,
|
|
3081
|
+
connectionId,
|
|
3082
|
+
{
|
|
3083
|
+
subscriptionType: "feed",
|
|
3084
|
+
entityType,
|
|
3085
|
+
entityId,
|
|
3086
|
+
feedTypes,
|
|
3087
|
+
subscribedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3088
|
+
expiresAt
|
|
3089
|
+
}
|
|
3090
|
+
);
|
|
3091
|
+
}
|
|
3092
|
+
return { statusCode: 200, body: "Connected" };
|
|
3093
|
+
} catch (error) {
|
|
3094
|
+
console.error("Error in $connect:", error);
|
|
3095
|
+
return { statusCode: 500, body: "Failed to connect" };
|
|
3096
|
+
}
|
|
3097
|
+
});
|
|
3098
|
+
var disconnect = (container) => (event) => __async(null, null, function* () {
|
|
3099
|
+
const connectionId = event.requestContext.connectionId;
|
|
3100
|
+
if (!connectionId) {
|
|
3101
|
+
return { statusCode: 400, body: "Missing connection ID" };
|
|
3102
|
+
}
|
|
3103
|
+
const wsRepo = container.websocketRepository;
|
|
3104
|
+
try {
|
|
3105
|
+
const subscriptions = yield wsRepo.querySubscriptionsByConnectionId(connectionId);
|
|
3106
|
+
const deletePromises = [];
|
|
3107
|
+
for (const item of subscriptions) {
|
|
3108
|
+
if (item.PK && item.SK) {
|
|
3109
|
+
deletePromises.push(
|
|
3110
|
+
wsRepo.deleteSubscription(item.PK, item.connectionId).catch(
|
|
3111
|
+
(e) => console.warn("Failed to delete subscription on disconnect:", e)
|
|
3112
|
+
)
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
deletePromises.push(wsRepo.deleteConnection(connectionId));
|
|
3117
|
+
yield Promise.all(deletePromises);
|
|
3118
|
+
return { statusCode: 200, body: "Disconnected" };
|
|
3119
|
+
} catch (error) {
|
|
3120
|
+
console.error("Error cleaning up connection:", error);
|
|
3121
|
+
return { statusCode: 500, body: "Failed to disconnect" };
|
|
3122
|
+
}
|
|
3123
|
+
});
|
|
3124
|
+
var $default = (container) => (event) => __async(null, null, function* () {
|
|
3125
|
+
const connectionId = event.requestContext.connectionId;
|
|
3126
|
+
if (!connectionId || !event.body) {
|
|
3127
|
+
return { statusCode: 400, body: "Invalid message" };
|
|
3128
|
+
}
|
|
3129
|
+
let message;
|
|
3130
|
+
try {
|
|
3131
|
+
message = JSON.parse(event.body);
|
|
3132
|
+
} catch (e) {
|
|
3133
|
+
return { statusCode: 400, body: "Invalid JSON" };
|
|
3134
|
+
}
|
|
3135
|
+
const wsRepo = container.websocketRepository;
|
|
3136
|
+
const wsEndpoint = getWsEndpoint();
|
|
3137
|
+
const managementApi = new ApiGatewayManagementApiClient({
|
|
3138
|
+
endpoint: wsEndpoint
|
|
3139
|
+
});
|
|
3140
|
+
try {
|
|
3141
|
+
switch (message.action) {
|
|
3142
|
+
case "subscribe": {
|
|
3143
|
+
const {
|
|
3144
|
+
entityType,
|
|
3145
|
+
byEntityType,
|
|
3146
|
+
byEntityId,
|
|
3147
|
+
mutualEntityType,
|
|
3148
|
+
channel
|
|
3149
|
+
} = message.payload;
|
|
3150
|
+
if (entityType && !byEntityType) {
|
|
3151
|
+
yield wsRepo.createSubscription(
|
|
3152
|
+
`${SUB_ENTITY_TYPE}${entityType}`,
|
|
3153
|
+
connectionId,
|
|
3154
|
+
{
|
|
3155
|
+
subscriptionType: "entity-type",
|
|
3156
|
+
entityType,
|
|
3157
|
+
subscribedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3158
|
+
}
|
|
3159
|
+
);
|
|
3160
|
+
} else if (byEntityType && byEntityId && mutualEntityType) {
|
|
3161
|
+
yield wsRepo.createSubscription(
|
|
3162
|
+
`${SUB_MUTUAL_TYPE}${byEntityType}#${byEntityId}#${mutualEntityType}`,
|
|
3163
|
+
connectionId,
|
|
3164
|
+
{
|
|
3165
|
+
subscriptionType: "mutual-type",
|
|
3166
|
+
byEntityType,
|
|
3167
|
+
byEntityId,
|
|
3168
|
+
entityType: mutualEntityType,
|
|
3169
|
+
subscribedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3170
|
+
}
|
|
3171
|
+
);
|
|
3172
|
+
} else if (channel) {
|
|
3173
|
+
yield wsRepo.createSubscription(
|
|
3174
|
+
`${SUB_EPHEMERAL}${channel}`,
|
|
3175
|
+
connectionId,
|
|
3176
|
+
{
|
|
3177
|
+
subscriptionType: "ephemeral",
|
|
3178
|
+
channel,
|
|
3179
|
+
subscribedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3180
|
+
}
|
|
3181
|
+
);
|
|
3182
|
+
} else {
|
|
3183
|
+
return { statusCode: 400, body: "Invalid subscription parameters" };
|
|
3184
|
+
}
|
|
3185
|
+
const ackMessage = {
|
|
3186
|
+
type: "ack",
|
|
3187
|
+
id: message.id,
|
|
3188
|
+
payload: { action: "subscribe", success: true }
|
|
3189
|
+
};
|
|
3190
|
+
yield managementApi.send(
|
|
3191
|
+
new PostToConnectionCommand({
|
|
3192
|
+
ConnectionId: connectionId,
|
|
3193
|
+
Data: JSON.stringify(ackMessage)
|
|
3194
|
+
})
|
|
3195
|
+
);
|
|
3196
|
+
return { statusCode: 200, body: "Subscribed" };
|
|
3197
|
+
}
|
|
3198
|
+
case "unsubscribe": {
|
|
3199
|
+
const {
|
|
3200
|
+
entityType,
|
|
3201
|
+
byEntityType,
|
|
3202
|
+
byEntityId,
|
|
3203
|
+
mutualEntityType,
|
|
3204
|
+
channel
|
|
3205
|
+
} = message.payload;
|
|
3206
|
+
if (entityType && !byEntityType) {
|
|
3207
|
+
yield wsRepo.deleteSubscription(
|
|
3208
|
+
`${SUB_ENTITY_TYPE}${entityType}`,
|
|
3209
|
+
connectionId
|
|
3210
|
+
);
|
|
3211
|
+
} else if (byEntityType && byEntityId && mutualEntityType) {
|
|
3212
|
+
yield wsRepo.deleteSubscription(
|
|
3213
|
+
`${SUB_MUTUAL_TYPE}${byEntityType}#${byEntityId}#${mutualEntityType}`,
|
|
3214
|
+
connectionId
|
|
3215
|
+
);
|
|
3216
|
+
} else if (channel) {
|
|
3217
|
+
yield wsRepo.deleteSubscription(
|
|
3218
|
+
`${SUB_EPHEMERAL}${channel}`,
|
|
3219
|
+
connectionId
|
|
3220
|
+
);
|
|
3221
|
+
}
|
|
3222
|
+
const ackMessage = {
|
|
3223
|
+
type: "ack",
|
|
3224
|
+
id: message.id,
|
|
3225
|
+
payload: { action: "unsubscribe", success: true }
|
|
3226
|
+
};
|
|
3227
|
+
yield managementApi.send(
|
|
3228
|
+
new PostToConnectionCommand({
|
|
3229
|
+
ConnectionId: connectionId,
|
|
3230
|
+
Data: JSON.stringify(ackMessage)
|
|
3231
|
+
})
|
|
3232
|
+
);
|
|
3233
|
+
return { statusCode: 200, body: "Unsubscribed" };
|
|
3234
|
+
}
|
|
3235
|
+
case "ping": {
|
|
3236
|
+
const pongMessage = {
|
|
3237
|
+
type: "pong",
|
|
3238
|
+
id: message.id,
|
|
3239
|
+
payload: { timestamp: Date.now() }
|
|
3240
|
+
};
|
|
3241
|
+
yield managementApi.send(
|
|
3242
|
+
new PostToConnectionCommand({
|
|
3243
|
+
ConnectionId: connectionId,
|
|
3244
|
+
Data: JSON.stringify(pongMessage)
|
|
3245
|
+
})
|
|
3246
|
+
);
|
|
3247
|
+
return { statusCode: 200, body: "Pong" };
|
|
3248
|
+
}
|
|
3249
|
+
case "ephemeral": {
|
|
3250
|
+
const { channel, data } = message.payload;
|
|
3251
|
+
if (!channel) {
|
|
3252
|
+
return { statusCode: 400, body: "Missing channel" };
|
|
3253
|
+
}
|
|
3254
|
+
const conn = yield wsRepo.getConnection(connectionId);
|
|
3255
|
+
const senderId = conn == null ? void 0 : conn.entityId;
|
|
3256
|
+
const subKey = `${SUB_EPHEMERAL}${channel}`;
|
|
3257
|
+
const ephemeralMessage = {
|
|
3258
|
+
type: "ephemeral",
|
|
3259
|
+
id: ulid3(),
|
|
3260
|
+
payload: { channel, data, senderId }
|
|
3261
|
+
};
|
|
3262
|
+
yield broadcastToSubscribers(
|
|
3263
|
+
managementApi,
|
|
3264
|
+
wsRepo,
|
|
3265
|
+
subKey,
|
|
3266
|
+
ephemeralMessage,
|
|
3267
|
+
connectionId
|
|
3268
|
+
// Exclude sender
|
|
3269
|
+
);
|
|
3270
|
+
return { statusCode: 200, body: "Broadcasted" };
|
|
3271
|
+
}
|
|
3272
|
+
default:
|
|
3273
|
+
return { statusCode: 400, body: "Unknown action" };
|
|
3274
|
+
}
|
|
3275
|
+
} catch (error) {
|
|
3276
|
+
console.error("Error handling message:", error);
|
|
3277
|
+
try {
|
|
3278
|
+
const errorMessage = {
|
|
3279
|
+
type: "error",
|
|
3280
|
+
id: message.id,
|
|
3281
|
+
payload: { message: "Internal server error" }
|
|
3282
|
+
};
|
|
3283
|
+
yield managementApi.send(
|
|
3284
|
+
new PostToConnectionCommand({
|
|
3285
|
+
ConnectionId: connectionId,
|
|
3286
|
+
Data: JSON.stringify(errorMessage)
|
|
3287
|
+
})
|
|
3288
|
+
);
|
|
3289
|
+
} catch (e) {
|
|
3290
|
+
}
|
|
3291
|
+
return { statusCode: 500, body: "Internal server error" };
|
|
3292
|
+
}
|
|
3293
|
+
});
|
|
3294
|
+
var broadcast = (container) => (event) => __async(null, null, function* () {
|
|
3295
|
+
var _a, _b, _c, _d;
|
|
3296
|
+
const wsRepo = container.websocketRepository;
|
|
3297
|
+
const wsEndpoint = getWsEndpoint();
|
|
3298
|
+
const managementApi = new ApiGatewayManagementApiClient({
|
|
3299
|
+
endpoint: wsEndpoint
|
|
3300
|
+
});
|
|
3301
|
+
for (const record of event.Records) {
|
|
3302
|
+
const isInsert = record.eventName === "INSERT";
|
|
3303
|
+
const isModify = record.eventName === "MODIFY";
|
|
3304
|
+
const isRemove = record.eventName === "REMOVE";
|
|
3305
|
+
if (!isInsert && !isModify && !isRemove) continue;
|
|
3306
|
+
const newImage = (_a = record.dynamodb) == null ? void 0 : _a.NewImage;
|
|
3307
|
+
const oldImage = (_b = record.dynamodb) == null ? void 0 : _b.OldImage;
|
|
3308
|
+
const image = newImage || oldImage;
|
|
3309
|
+
if (!image) continue;
|
|
3310
|
+
const pk = ((_c = image.PK) == null ? void 0 : _c.S) || "";
|
|
3311
|
+
const sk = ((_d = image.SK) == null ? void 0 : _d.S) || "";
|
|
3312
|
+
const pkParts = pk.split("#");
|
|
3313
|
+
if (pkParts.length < 2) continue;
|
|
3314
|
+
const firstPart = pkParts[0];
|
|
3315
|
+
if (firstPart === firstPart.toUpperCase() || firstPart.includes(":")) {
|
|
3316
|
+
continue;
|
|
3317
|
+
}
|
|
3318
|
+
const entityType = pkParts[0];
|
|
3319
|
+
const entityId = pkParts[1];
|
|
3320
|
+
const isMutual = !sk.startsWith("#METADATA#") && sk.includes("#");
|
|
3321
|
+
try {
|
|
3322
|
+
if (isMutual) {
|
|
3323
|
+
const skParts = sk.split("#");
|
|
3324
|
+
const mutualEntityType = skParts[0];
|
|
3325
|
+
const byEntityId = entityId;
|
|
3326
|
+
const subKey = `${SUB_MUTUAL_TYPE}${entityType}#${byEntityId}#${mutualEntityType}`;
|
|
3327
|
+
const subscribers = yield wsRepo.querySubscriptionsByKey(subKey);
|
|
3328
|
+
if (subscribers.length) {
|
|
3329
|
+
let eventType;
|
|
3330
|
+
if (isInsert) eventType = "mutual.created";
|
|
3331
|
+
else if (isModify) eventType = "mutual.updated";
|
|
3332
|
+
else eventType = "mutual.deleted";
|
|
3333
|
+
const message = {
|
|
3334
|
+
type: eventType,
|
|
3335
|
+
id: ulid3(),
|
|
3336
|
+
payload: {
|
|
3337
|
+
byEntityType: entityType,
|
|
3338
|
+
byEntityId,
|
|
3339
|
+
mutualEntityType,
|
|
3340
|
+
entityId: skParts[1],
|
|
3341
|
+
data: isRemove ? void 0 : unmarshall4(image)
|
|
3342
|
+
}
|
|
3343
|
+
};
|
|
3344
|
+
yield broadcastToSubscribers(
|
|
3345
|
+
managementApi,
|
|
3346
|
+
wsRepo,
|
|
3347
|
+
subKey,
|
|
3348
|
+
message
|
|
3349
|
+
);
|
|
3350
|
+
}
|
|
3351
|
+
} else {
|
|
3352
|
+
const subKey = `${SUB_ENTITY_TYPE}${entityType}`;
|
|
3353
|
+
const subscribers = yield wsRepo.querySubscriptionsByKey(subKey);
|
|
3354
|
+
if (subscribers.length) {
|
|
3355
|
+
let eventType;
|
|
3356
|
+
if (isInsert) eventType = "entity.created";
|
|
3357
|
+
else if (isModify) eventType = "entity.updated";
|
|
3358
|
+
else eventType = "entity.deleted";
|
|
3359
|
+
const message = {
|
|
3360
|
+
type: eventType,
|
|
3361
|
+
id: ulid3(),
|
|
3362
|
+
payload: {
|
|
3363
|
+
entityType,
|
|
3364
|
+
entityId,
|
|
3365
|
+
data: isRemove ? void 0 : unmarshall4(image)
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
yield broadcastToSubscribers(
|
|
3369
|
+
managementApi,
|
|
3370
|
+
wsRepo,
|
|
3371
|
+
subKey,
|
|
3372
|
+
message
|
|
3373
|
+
);
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
yield broadcastToFeedSubscribers(
|
|
3377
|
+
managementApi,
|
|
3378
|
+
wsRepo,
|
|
3379
|
+
entityType,
|
|
3380
|
+
entityId,
|
|
3381
|
+
isMutual ? sk.split("#")[0] : entityType,
|
|
3382
|
+
// the changed entity type
|
|
3383
|
+
isMutual ? {
|
|
3384
|
+
type: isInsert ? "mutual.created" : isModify ? "mutual.updated" : "mutual.deleted",
|
|
3385
|
+
id: ulid3(),
|
|
3386
|
+
payload: {
|
|
3387
|
+
byEntityType: entityType,
|
|
3388
|
+
byEntityId: entityId,
|
|
3389
|
+
mutualEntityType: sk.split("#")[0],
|
|
3390
|
+
entityId: sk.split("#")[1],
|
|
3391
|
+
data: isRemove ? void 0 : unmarshall4(image)
|
|
3392
|
+
}
|
|
3393
|
+
} : {
|
|
3394
|
+
type: isInsert ? "entity.created" : isModify ? "entity.updated" : "entity.deleted",
|
|
3395
|
+
id: ulid3(),
|
|
3396
|
+
payload: {
|
|
3397
|
+
entityType,
|
|
3398
|
+
entityId,
|
|
3399
|
+
data: isRemove ? void 0 : unmarshall4(image)
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
);
|
|
3403
|
+
} catch (error) {
|
|
3404
|
+
console.error("Error broadcasting:", error);
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
});
|
|
3408
|
+
function broadcastToSubscribers(managementApi, wsRepo, subKey, message, excludeConnectionId) {
|
|
3409
|
+
return __async(this, null, function* () {
|
|
3410
|
+
const subscribers = yield wsRepo.querySubscriptionsByKey(subKey);
|
|
3411
|
+
if (!subscribers.length) return;
|
|
3412
|
+
const messageData = JSON.stringify(message);
|
|
3413
|
+
const sends = subscribers.filter((subscriber) => {
|
|
3414
|
+
const id = subscriber.connectionId;
|
|
3415
|
+
return !excludeConnectionId || id !== excludeConnectionId;
|
|
3416
|
+
}).map((subscriber) => __async(null, null, function* () {
|
|
3417
|
+
var _a;
|
|
3418
|
+
try {
|
|
3419
|
+
yield managementApi.send(
|
|
3420
|
+
new PostToConnectionCommand({
|
|
3421
|
+
ConnectionId: subscriber.connectionId,
|
|
3422
|
+
Data: messageData
|
|
3423
|
+
})
|
|
3424
|
+
);
|
|
3425
|
+
} catch (error) {
|
|
3426
|
+
const isGone = (error == null ? void 0 : error.name) === "GoneException" || ((_a = error == null ? void 0 : error.$metadata) == null ? void 0 : _a.httpStatusCode) === 410;
|
|
3427
|
+
if (isGone) {
|
|
3428
|
+
yield wsRepo.deleteSubscription(subKey, subscriber.connectionId).catch(
|
|
3429
|
+
(e) => console.warn("Failed to clean up stale subscription:", e)
|
|
3430
|
+
);
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
}));
|
|
3434
|
+
yield Promise.allSettled(sends);
|
|
3435
|
+
});
|
|
3436
|
+
}
|
|
3437
|
+
function broadcastToFeedSubscribers(managementApi, wsRepo, byEntityType, byEntityId, changedEntityType, message) {
|
|
3438
|
+
return __async(this, null, function* () {
|
|
3439
|
+
var _a;
|
|
3440
|
+
const connections = yield wsRepo.queryMutualConnections(
|
|
3441
|
+
byEntityType,
|
|
3442
|
+
byEntityId
|
|
3443
|
+
);
|
|
3444
|
+
if (!connections.length) return;
|
|
3445
|
+
const connectedEntities = new Set(
|
|
3446
|
+
connections.map((c) => `${c.entityType}:${c.entityId}`)
|
|
3447
|
+
);
|
|
3448
|
+
connectedEntities.add(`${byEntityType}:${byEntityId}`);
|
|
3449
|
+
const sentConnections = /* @__PURE__ */ new Set();
|
|
3450
|
+
for (const connEntity of connectedEntities) {
|
|
3451
|
+
const [entityType, entityId] = connEntity.split(":");
|
|
3452
|
+
const feedSubs = yield wsRepo.queryFeedSubscriptions(entityType, entityId);
|
|
3453
|
+
if (!feedSubs.length) continue;
|
|
3454
|
+
for (const feedSub of feedSubs) {
|
|
3455
|
+
const feedTypes = feedSub.feedTypes;
|
|
3456
|
+
const connectionId = feedSub.connectionId;
|
|
3457
|
+
if (feedTypes && !feedTypes.includes(changedEntityType)) continue;
|
|
3458
|
+
if (sentConnections.has(connectionId)) continue;
|
|
3459
|
+
sentConnections.add(connectionId);
|
|
3460
|
+
try {
|
|
3461
|
+
yield managementApi.send(
|
|
3462
|
+
new PostToConnectionCommand({
|
|
3463
|
+
ConnectionId: connectionId,
|
|
3464
|
+
Data: JSON.stringify(message)
|
|
3465
|
+
})
|
|
3466
|
+
);
|
|
3467
|
+
} catch (error) {
|
|
3468
|
+
const isGone = (error == null ? void 0 : error.name) === "GoneException" || ((_a = error == null ? void 0 : error.$metadata) == null ? void 0 : _a.httpStatusCode) === 410;
|
|
3469
|
+
if (isGone) {
|
|
3470
|
+
yield wsRepo.deleteSubscription(
|
|
3471
|
+
`SUB#FEED#${entityType}#${entityId}`,
|
|
3472
|
+
connectionId
|
|
3473
|
+
).catch(
|
|
3474
|
+
(e) => console.warn("Failed to clean up stale feed subscription:", e)
|
|
3475
|
+
);
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
});
|
|
3481
|
+
}
|
|
3482
|
+
|
|
2858
3483
|
// services/DependencyContainer.ts
|
|
2859
3484
|
import { DynamoDB } from "@aws-sdk/client-dynamodb";
|
|
2860
3485
|
|
|
@@ -7091,7 +7716,16 @@ var UpdateEntityController = class {
|
|
|
7091
7716
|
const accountId = c.req.header("account-id");
|
|
7092
7717
|
const { entityType, entityId } = c.req.param();
|
|
7093
7718
|
const body = yield c.req.json();
|
|
7094
|
-
const _a = body, { $where: where } = _a, entityPayload = __objRest(_a, ["$where"]);
|
|
7719
|
+
const _a = body, { $condition: condition, $where: where } = _a, entityPayload = __objRest(_a, ["$condition", "$where"]);
|
|
7720
|
+
if (condition !== void 0) {
|
|
7721
|
+
if (typeof condition !== "string" || condition.trim().length === 0) {
|
|
7722
|
+
c.status(httpStatus8.BAD_REQUEST);
|
|
7723
|
+
return c.json({
|
|
7724
|
+
code: "API_VALIDATION_ERROR",
|
|
7725
|
+
message: "$condition must be a non-empty string"
|
|
7726
|
+
});
|
|
7727
|
+
}
|
|
7728
|
+
}
|
|
7095
7729
|
const errorContext = {
|
|
7096
7730
|
accountId,
|
|
7097
7731
|
"req.params": c.req.param(),
|
|
@@ -7103,6 +7737,7 @@ var UpdateEntityController = class {
|
|
|
7103
7737
|
entityId,
|
|
7104
7738
|
entityPayload,
|
|
7105
7739
|
accountId,
|
|
7740
|
+
condition,
|
|
7106
7741
|
where
|
|
7107
7742
|
});
|
|
7108
7743
|
errorContext.entity = entity;
|
|
@@ -7121,6 +7756,10 @@ var UpdateEntityController = class {
|
|
|
7121
7756
|
c.status(httpStatus8.NOT_FOUND);
|
|
7122
7757
|
return c.json(__spreadValues({}, err.toJSON()));
|
|
7123
7758
|
}
|
|
7759
|
+
if (err instanceof StandardError && err.code === StandardErrorCode.INVALID_CONDITION) {
|
|
7760
|
+
c.status(httpStatus8.BAD_REQUEST);
|
|
7761
|
+
return c.json(__spreadValues({}, err.toJSON()));
|
|
7762
|
+
}
|
|
7124
7763
|
if (err instanceof StandardError && err.code === StandardErrorCode.UNIQUE_VALUE_EXISTS) {
|
|
7125
7764
|
c.status(httpStatus8.BAD_REQUEST);
|
|
7126
7765
|
return c.json(__spreadValues({}, err.toJSON()));
|
|
@@ -7147,11 +7786,21 @@ var AdjustEntityController = class {
|
|
|
7147
7786
|
constructor(entityService) {
|
|
7148
7787
|
this.entityService = entityService;
|
|
7149
7788
|
this.controller = createMiddleware9((c) => __async(this, null, function* () {
|
|
7150
|
-
var
|
|
7789
|
+
var _b;
|
|
7151
7790
|
const accountId = c.req.header("account-id") || "";
|
|
7152
7791
|
const { entityType, entityId } = c.req.param();
|
|
7153
7792
|
const body = yield c.req.json();
|
|
7154
|
-
|
|
7793
|
+
const _a = body, { $condition: condition } = _a, adjustments = __objRest(_a, ["$condition"]);
|
|
7794
|
+
if (condition !== void 0) {
|
|
7795
|
+
if (typeof condition !== "string" || condition.trim().length === 0) {
|
|
7796
|
+
c.status(httpStatus9.BAD_REQUEST);
|
|
7797
|
+
return c.json({
|
|
7798
|
+
code: "API_VALIDATION_ERROR",
|
|
7799
|
+
message: "$condition must be a non-empty string"
|
|
7800
|
+
});
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
7803
|
+
for (const [key, value] of Object.entries(adjustments)) {
|
|
7155
7804
|
if (typeof value !== "number") {
|
|
7156
7805
|
c.status(httpStatus9.BAD_REQUEST);
|
|
7157
7806
|
return c.json({
|
|
@@ -7164,8 +7813,9 @@ var AdjustEntityController = class {
|
|
|
7164
7813
|
const entity = yield this.entityService.adjustEntity({
|
|
7165
7814
|
entityType,
|
|
7166
7815
|
entityId,
|
|
7167
|
-
adjustments
|
|
7168
|
-
accountId
|
|
7816
|
+
adjustments,
|
|
7817
|
+
accountId,
|
|
7818
|
+
condition
|
|
7169
7819
|
});
|
|
7170
7820
|
c.status(httpStatus9.OK);
|
|
7171
7821
|
return c.json(entity.toJSON());
|
|
@@ -7174,7 +7824,11 @@ var AdjustEntityController = class {
|
|
|
7174
7824
|
c.status(httpStatus9.NOT_FOUND);
|
|
7175
7825
|
return c.json(__spreadValues({}, err.toJSON()));
|
|
7176
7826
|
}
|
|
7177
|
-
if (
|
|
7827
|
+
if (err instanceof StandardError && err.code === StandardErrorCode.INVALID_CONDITION) {
|
|
7828
|
+
c.status(httpStatus9.BAD_REQUEST);
|
|
7829
|
+
return c.json(__spreadValues({}, err.toJSON()));
|
|
7830
|
+
}
|
|
7831
|
+
if ((err == null ? void 0 : err.name) === "ConditionalCheckFailedException" || ((_b = err == null ? void 0 : err.__type) == null ? void 0 : _b.includes("ConditionalCheckFailed"))) {
|
|
7178
7832
|
c.status(httpStatus9.CONFLICT);
|
|
7179
7833
|
return c.json({
|
|
7180
7834
|
code: "ADJUSTMENT_CONSTRAINT_VIOLATED",
|
|
@@ -7479,6 +8133,9 @@ var UpdateMutualController = class {
|
|
|
7479
8133
|
}
|
|
7480
8134
|
};
|
|
7481
8135
|
|
|
8136
|
+
// services/entity.service.ts
|
|
8137
|
+
import { marshall as marshall6 } from "@aws-sdk/util-dynamodb";
|
|
8138
|
+
|
|
7482
8139
|
// data/utils/build-condition-expression.ts
|
|
7483
8140
|
import { marshall as marshall5 } from "@aws-sdk/util-dynamodb";
|
|
7484
8141
|
function buildConditionExpression(where) {
|
|
@@ -7540,6 +8197,55 @@ function buildConditionExpression(where) {
|
|
|
7540
8197
|
};
|
|
7541
8198
|
}
|
|
7542
8199
|
|
|
8200
|
+
// services/resolve-condition.ts
|
|
8201
|
+
function resolveAdjustmentCondition(_0) {
|
|
8202
|
+
return __async(this, arguments, function* ({
|
|
8203
|
+
conditionName,
|
|
8204
|
+
conditions,
|
|
8205
|
+
adjustments,
|
|
8206
|
+
getEntityData
|
|
8207
|
+
}) {
|
|
8208
|
+
if (!Object.hasOwn(conditions, conditionName)) {
|
|
8209
|
+
throw new StandardError(
|
|
8210
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
8211
|
+
`Unknown adjustment condition: '${conditionName}'`
|
|
8212
|
+
);
|
|
8213
|
+
}
|
|
8214
|
+
const condition = conditions[conditionName];
|
|
8215
|
+
let resolved;
|
|
8216
|
+
if (typeof condition === "function") {
|
|
8217
|
+
const data = yield getEntityData();
|
|
8218
|
+
resolved = condition(data, adjustments);
|
|
8219
|
+
} else {
|
|
8220
|
+
resolved = condition;
|
|
8221
|
+
}
|
|
8222
|
+
return buildConditionExpression(resolved);
|
|
8223
|
+
});
|
|
8224
|
+
}
|
|
8225
|
+
function resolveUpdateCondition(_0) {
|
|
8226
|
+
return __async(this, arguments, function* ({
|
|
8227
|
+
conditionName,
|
|
8228
|
+
conditions,
|
|
8229
|
+
getEntityData
|
|
8230
|
+
}) {
|
|
8231
|
+
if (!Object.hasOwn(conditions, conditionName)) {
|
|
8232
|
+
throw new StandardError(
|
|
8233
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
8234
|
+
`Unknown update condition: '${conditionName}'`
|
|
8235
|
+
);
|
|
8236
|
+
}
|
|
8237
|
+
const condition = conditions[conditionName];
|
|
8238
|
+
let resolved;
|
|
8239
|
+
if (typeof condition === "function") {
|
|
8240
|
+
const data = yield getEntityData();
|
|
8241
|
+
resolved = condition(data);
|
|
8242
|
+
} else {
|
|
8243
|
+
resolved = condition;
|
|
8244
|
+
}
|
|
8245
|
+
return buildConditionExpression(resolved);
|
|
8246
|
+
});
|
|
8247
|
+
}
|
|
8248
|
+
|
|
7543
8249
|
// services/entity.service.ts
|
|
7544
8250
|
var EntityService = class {
|
|
7545
8251
|
constructor(EntityConfig, EmailAuthEnabledEntities, entityRepository, publishEvent2, entityServiceLifeCycle) {
|
|
@@ -7595,34 +8301,59 @@ var EntityService = class {
|
|
|
7595
8301
|
entityType,
|
|
7596
8302
|
entityId,
|
|
7597
8303
|
adjustments,
|
|
7598
|
-
accountId
|
|
8304
|
+
accountId,
|
|
8305
|
+
condition
|
|
7599
8306
|
}) {
|
|
7600
|
-
var _a, _b, _c
|
|
7601
|
-
const
|
|
7602
|
-
|
|
7603
|
-
|
|
8307
|
+
var _a, _b, _c;
|
|
8308
|
+
const entityConfig = this.EntityConfig[entityType];
|
|
8309
|
+
const adjustmentConditions = entityConfig == null ? void 0 : entityConfig.adjustmentConditions;
|
|
8310
|
+
const rawConstraints = entityConfig == null ? void 0 : entityConfig.adjustmentConstraints;
|
|
8311
|
+
let opts;
|
|
8312
|
+
if (adjustmentConditions) {
|
|
8313
|
+
if (!condition) {
|
|
8314
|
+
throw new StandardError(
|
|
8315
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
8316
|
+
"Entity has adjustmentConditions defined; $condition is required for adjustEntity"
|
|
8317
|
+
);
|
|
8318
|
+
}
|
|
8319
|
+
opts = yield resolveAdjustmentCondition({
|
|
8320
|
+
conditionName: condition,
|
|
8321
|
+
conditions: adjustmentConditions,
|
|
8322
|
+
adjustments,
|
|
8323
|
+
getEntityData: () => __async(this, null, function* () {
|
|
8324
|
+
var _a2;
|
|
8325
|
+
const entity2 = yield this.entityRepository.getEntity(entityType, entityId);
|
|
8326
|
+
return (_a2 = entity2 == null ? void 0 : entity2.data) != null ? _a2 : {};
|
|
8327
|
+
})
|
|
8328
|
+
});
|
|
8329
|
+
} else if (rawConstraints) {
|
|
8330
|
+
console.warn(
|
|
8331
|
+
"[monorise] adjustmentConstraints is deprecated. Use adjustmentConditions instead."
|
|
8332
|
+
);
|
|
8333
|
+
let resolvedConstraints = rawConstraints;
|
|
7604
8334
|
const hasDynamicFields = Object.values(rawConstraints).some(
|
|
7605
8335
|
(c) => c.minField || c.maxField
|
|
7606
8336
|
);
|
|
7607
8337
|
if (hasDynamicFields) {
|
|
7608
8338
|
const currentEntity = yield this.entityRepository.getEntity(entityType, entityId);
|
|
7609
|
-
const data = (
|
|
8339
|
+
const data = (_a = currentEntity == null ? void 0 : currentEntity.data) != null ? _a : {};
|
|
7610
8340
|
resolvedConstraints = {};
|
|
7611
8341
|
for (const [field, constraint] of Object.entries(rawConstraints)) {
|
|
7612
8342
|
const resolved = {};
|
|
7613
8343
|
if (constraint.min !== void 0) resolved.min = constraint.min;
|
|
7614
8344
|
if (constraint.max !== void 0) resolved.max = constraint.max;
|
|
7615
|
-
if (constraint.minField) resolved.min = (
|
|
7616
|
-
if (constraint.maxField) resolved.max = (
|
|
8345
|
+
if (constraint.minField) resolved.min = (_b = data[constraint.minField]) != null ? _b : 0;
|
|
8346
|
+
if (constraint.maxField) resolved.max = (_c = data[constraint.maxField]) != null ? _c : Number.MAX_SAFE_INTEGER;
|
|
7617
8347
|
resolvedConstraints[field] = resolved;
|
|
7618
8348
|
}
|
|
7619
8349
|
}
|
|
8350
|
+
opts = this.buildLegacyAdjustCondition(adjustments, resolvedConstraints);
|
|
7620
8351
|
}
|
|
7621
8352
|
const entity = yield this.entityRepository.adjustEntity(
|
|
7622
8353
|
entityType,
|
|
7623
8354
|
entityId,
|
|
7624
8355
|
adjustments,
|
|
7625
|
-
|
|
8356
|
+
opts
|
|
7626
8357
|
);
|
|
7627
8358
|
yield this.publishEvent({
|
|
7628
8359
|
event: EVENT.CORE.ENTITY_UPDATED,
|
|
@@ -7641,9 +8372,10 @@ var EntityService = class {
|
|
|
7641
8372
|
entityId,
|
|
7642
8373
|
entityPayload,
|
|
7643
8374
|
accountId,
|
|
8375
|
+
condition,
|
|
7644
8376
|
where
|
|
7645
8377
|
}) {
|
|
7646
|
-
var _a, _b;
|
|
8378
|
+
var _a, _b, _c;
|
|
7647
8379
|
const errorContext = {};
|
|
7648
8380
|
try {
|
|
7649
8381
|
const entitySchema = this.EntityConfig[entityType].baseSchema;
|
|
@@ -7657,7 +8389,30 @@ var EntityService = class {
|
|
|
7657
8389
|
const parsedEntityPayload = entitySchema.parse(entityPayload);
|
|
7658
8390
|
const parsedMutualPayload = mutualSchema == null ? void 0 : mutualSchema.parse(entityPayload);
|
|
7659
8391
|
errorContext.parsedMutualPayload = parsedMutualPayload;
|
|
7660
|
-
|
|
8392
|
+
let opts;
|
|
8393
|
+
if (condition) {
|
|
8394
|
+
const updateConditions = (_b = this.EntityConfig[entityType]) == null ? void 0 : _b.updateConditions;
|
|
8395
|
+
if (!updateConditions) {
|
|
8396
|
+
throw new StandardError(
|
|
8397
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
8398
|
+
`Entity '${entityType}' has no updateConditions defined`
|
|
8399
|
+
);
|
|
8400
|
+
}
|
|
8401
|
+
opts = yield resolveUpdateCondition({
|
|
8402
|
+
conditionName: condition,
|
|
8403
|
+
conditions: updateConditions,
|
|
8404
|
+
getEntityData: () => __async(this, null, function* () {
|
|
8405
|
+
var _a2;
|
|
8406
|
+
const entity2 = yield this.entityRepository.getEntity(entityType, entityId);
|
|
8407
|
+
return (_a2 = entity2 == null ? void 0 : entity2.data) != null ? _a2 : {};
|
|
8408
|
+
})
|
|
8409
|
+
});
|
|
8410
|
+
} else if (where && Object.keys(where).length > 0) {
|
|
8411
|
+
console.warn(
|
|
8412
|
+
"[monorise] $where is deprecated. Use named conditions via $condition instead."
|
|
8413
|
+
);
|
|
8414
|
+
opts = buildConditionExpression(where);
|
|
8415
|
+
}
|
|
7661
8416
|
const entity = yield this.entityRepository.updateEntity(
|
|
7662
8417
|
entityType,
|
|
7663
8418
|
entityId,
|
|
@@ -7670,7 +8425,7 @@ var EntityService = class {
|
|
|
7670
8425
|
const byEntityId = entityId;
|
|
7671
8426
|
const publishEventPromises = [];
|
|
7672
8427
|
for (const [fieldKey, config] of Object.entries(
|
|
7673
|
-
((
|
|
8428
|
+
((_c = this.EntityConfig[entityType].mutual) == null ? void 0 : _c.mutualFields) || {}
|
|
7674
8429
|
)) {
|
|
7675
8430
|
const toMutualIds = config.toMutualIds;
|
|
7676
8431
|
const mutualPayload = parsedMutualPayload[fieldKey];
|
|
@@ -7726,12 +8481,42 @@ var EntityService = class {
|
|
|
7726
8481
|
});
|
|
7727
8482
|
});
|
|
7728
8483
|
}
|
|
8484
|
+
/** @deprecated Converts legacy adjustmentConstraints to condition expression opts. */
|
|
8485
|
+
buildLegacyAdjustCondition(adjustments, constraints) {
|
|
8486
|
+
const conditionParts = [];
|
|
8487
|
+
const names = { "#data": "data" };
|
|
8488
|
+
const values = {};
|
|
8489
|
+
for (const [field, constraint] of Object.entries(constraints)) {
|
|
8490
|
+
const delta = adjustments[field];
|
|
8491
|
+
if (delta === void 0) continue;
|
|
8492
|
+
const namePlaceholder = `#where_${field}`;
|
|
8493
|
+
names[namePlaceholder] = field;
|
|
8494
|
+
const fieldRef = `#data.${namePlaceholder}`;
|
|
8495
|
+
if (constraint.min !== void 0 && delta < 0) {
|
|
8496
|
+
const valKey = `:where_${field}_min_threshold`;
|
|
8497
|
+
conditionParts.push(`${fieldRef} >= ${valKey}`);
|
|
8498
|
+
values[valKey] = constraint.min - delta;
|
|
8499
|
+
}
|
|
8500
|
+
if (constraint.max !== void 0 && delta > 0) {
|
|
8501
|
+
const valKey = `:where_${field}_max_threshold`;
|
|
8502
|
+
conditionParts.push(`${fieldRef} <= ${valKey}`);
|
|
8503
|
+
values[valKey] = constraint.max - delta;
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8506
|
+
if (conditionParts.length === 0) return void 0;
|
|
8507
|
+
return {
|
|
8508
|
+
ConditionExpression: conditionParts.join(" AND "),
|
|
8509
|
+
ExpressionAttributeNames: names,
|
|
8510
|
+
ExpressionAttributeValues: marshall6(values)
|
|
8511
|
+
};
|
|
8512
|
+
}
|
|
7729
8513
|
};
|
|
7730
8514
|
|
|
7731
8515
|
// services/mutual.service.ts
|
|
7732
|
-
import { ulid as
|
|
8516
|
+
import { ulid as ulid4 } from "ulid";
|
|
7733
8517
|
var MutualService = class {
|
|
7734
|
-
constructor(entityRepository, mutualRepository, publishEvent2, ddbUtils, entityServiceLifeCycle) {
|
|
8518
|
+
constructor(EntityConfig, entityRepository, mutualRepository, publishEvent2, ddbUtils, entityServiceLifeCycle) {
|
|
8519
|
+
this.EntityConfig = EntityConfig;
|
|
7735
8520
|
this.entityRepository = entityRepository;
|
|
7736
8521
|
this.mutualRepository = mutualRepository;
|
|
7737
8522
|
this.publishEvent = publishEvent2;
|
|
@@ -7746,6 +8531,7 @@ var MutualService = class {
|
|
|
7746
8531
|
accountId,
|
|
7747
8532
|
options = {}
|
|
7748
8533
|
}) {
|
|
8534
|
+
var _a;
|
|
7749
8535
|
const {
|
|
7750
8536
|
ensureEntityStrongConsistentWrite = false,
|
|
7751
8537
|
asEntity,
|
|
@@ -7766,7 +8552,7 @@ var MutualService = class {
|
|
|
7766
8552
|
options
|
|
7767
8553
|
}
|
|
7768
8554
|
};
|
|
7769
|
-
const schema = external_exports.record(external_exports.string(), external_exports.any());
|
|
8555
|
+
const schema = (_a = this.getMutualDataSchema(byEntityType, entityType)) != null ? _a : external_exports.record(external_exports.string(), external_exports.any());
|
|
7770
8556
|
const parsedMutualPayload = schema.parse(mutualPayload);
|
|
7771
8557
|
const [{ data: byEntityData }, { data: entityData }] = yield Promise.all([
|
|
7772
8558
|
this.entityRepository.getEntity(byEntityType, byEntityId),
|
|
@@ -7789,7 +8575,7 @@ var MutualService = class {
|
|
|
7789
8575
|
entityId,
|
|
7790
8576
|
entityData,
|
|
7791
8577
|
parsedMutualPayload,
|
|
7792
|
-
mutualId ||
|
|
8578
|
+
mutualId || ulid4(),
|
|
7793
8579
|
currentDatetime,
|
|
7794
8580
|
currentDatetime,
|
|
7795
8581
|
currentDatetime
|
|
@@ -7869,7 +8655,8 @@ var MutualService = class {
|
|
|
7869
8655
|
accountId,
|
|
7870
8656
|
options
|
|
7871
8657
|
}) {
|
|
7872
|
-
|
|
8658
|
+
var _a;
|
|
8659
|
+
const schema = (_a = this.getMutualDataSchema(byEntityType, entityType)) != null ? _a : external_exports.record(external_exports.string(), external_exports.any());
|
|
7873
8660
|
const parsedMutualPayload = schema.parse(mutualPayload);
|
|
7874
8661
|
const mutual = yield this.mutualRepository.updateMutual(
|
|
7875
8662
|
byEntityType,
|
|
@@ -7918,6 +8705,22 @@ var MutualService = class {
|
|
|
7918
8705
|
return mutual;
|
|
7919
8706
|
});
|
|
7920
8707
|
}
|
|
8708
|
+
getMutualDataSchema(byEntityType, entityType) {
|
|
8709
|
+
var _a, _b, _c;
|
|
8710
|
+
for (const [from, to] of [
|
|
8711
|
+
[byEntityType, entityType],
|
|
8712
|
+
[entityType, byEntityType]
|
|
8713
|
+
]) {
|
|
8714
|
+
const mutualFields = (_b = (_a = this.EntityConfig[from]) == null ? void 0 : _a.mutual) == null ? void 0 : _b.mutualFields;
|
|
8715
|
+
if (!mutualFields) continue;
|
|
8716
|
+
for (const config of Object.values(mutualFields)) {
|
|
8717
|
+
if (config.entityType === to && ((_c = config.mutual) == null ? void 0 : _c.mutualDataSchema)) {
|
|
8718
|
+
return config.mutual.mutualDataSchema;
|
|
8719
|
+
}
|
|
8720
|
+
}
|
|
8721
|
+
}
|
|
8722
|
+
return void 0;
|
|
8723
|
+
}
|
|
7921
8724
|
};
|
|
7922
8725
|
|
|
7923
8726
|
// controllers/tag/list-tags.controller.ts
|
|
@@ -7967,6 +8770,116 @@ var ListTagsController = class {
|
|
|
7967
8770
|
}
|
|
7968
8771
|
};
|
|
7969
8772
|
|
|
8773
|
+
// controllers/transaction/execute-transaction.controller.ts
|
|
8774
|
+
import { createMiddleware as createMiddleware17 } from "hono/factory";
|
|
8775
|
+
import httpStatus15 from "http-status";
|
|
8776
|
+
var ExecuteTransactionController = class {
|
|
8777
|
+
constructor(transactionService) {
|
|
8778
|
+
this.transactionService = transactionService;
|
|
8779
|
+
// biome-ignore lint/suspicious/noExplicitAny: Hono createMiddleware requires consistent return types
|
|
8780
|
+
this.controller = createMiddleware17((c) => __async(this, null, function* () {
|
|
8781
|
+
var _a;
|
|
8782
|
+
const accountId = c.req.header("account-id") || "";
|
|
8783
|
+
const body = yield c.req.json();
|
|
8784
|
+
if (!body.operations || !Array.isArray(body.operations)) {
|
|
8785
|
+
c.status(httpStatus15.BAD_REQUEST);
|
|
8786
|
+
return c.json({
|
|
8787
|
+
code: "API_VALIDATION_ERROR",
|
|
8788
|
+
message: 'Request body must contain an "operations" array'
|
|
8789
|
+
});
|
|
8790
|
+
}
|
|
8791
|
+
try {
|
|
8792
|
+
const result = yield this.transactionService.executeTransaction(
|
|
8793
|
+
body.operations,
|
|
8794
|
+
accountId
|
|
8795
|
+
);
|
|
8796
|
+
c.status(httpStatus15.OK);
|
|
8797
|
+
return c.json(result);
|
|
8798
|
+
} catch (err) {
|
|
8799
|
+
if (((_a = err.constructor) == null ? void 0 : _a.name) === "ZodError") {
|
|
8800
|
+
c.status(httpStatus15.BAD_REQUEST);
|
|
8801
|
+
return c.json({
|
|
8802
|
+
code: "API_VALIDATION_ERROR",
|
|
8803
|
+
message: "Validation failed",
|
|
8804
|
+
details: err.flatten()
|
|
8805
|
+
});
|
|
8806
|
+
}
|
|
8807
|
+
if (err instanceof StandardError) {
|
|
8808
|
+
const code = err.code;
|
|
8809
|
+
if (code === StandardErrorCode.TRANSACTION_EMPTY || code === StandardErrorCode.TRANSACTION_ITEM_LIMIT_EXCEEDED || code === StandardErrorCode.TRANSACTION_UNIQUE_FIELD_UPDATE || code === StandardErrorCode.INVALID_ENTITY_TYPE || code === StandardErrorCode.INVALID_CONDITION || code === StandardErrorCode.INVALID_UNIQUE_VALUE_TYPE) {
|
|
8810
|
+
c.status(httpStatus15.BAD_REQUEST);
|
|
8811
|
+
return c.json(__spreadValues({}, err.toJSON()));
|
|
8812
|
+
}
|
|
8813
|
+
if (code === StandardErrorCode.TRANSACTION_FAILED || code === StandardErrorCode.CONDITIONAL_CHECK_FAILED || code === StandardErrorCode.UNIQUE_VALUE_EXISTS) {
|
|
8814
|
+
c.status(httpStatus15.CONFLICT);
|
|
8815
|
+
return c.json(__spreadValues({}, err.toJSON()));
|
|
8816
|
+
}
|
|
8817
|
+
}
|
|
8818
|
+
throw err;
|
|
8819
|
+
}
|
|
8820
|
+
}));
|
|
8821
|
+
}
|
|
8822
|
+
};
|
|
8823
|
+
|
|
8824
|
+
// controllers/ws/create-ticket.controller.ts
|
|
8825
|
+
import { createMiddleware as createMiddleware18 } from "hono/factory";
|
|
8826
|
+
import { ulid as ulid5 } from "ulid";
|
|
8827
|
+
var TICKET_TTL_SECONDS = 30 * 60;
|
|
8828
|
+
var CreateTicketController = class {
|
|
8829
|
+
constructor(container) {
|
|
8830
|
+
this.container = container;
|
|
8831
|
+
this.controller = createMiddleware18((c) => __async(this, null, function* () {
|
|
8832
|
+
var _a;
|
|
8833
|
+
const { entityType, entityId } = c.req.param();
|
|
8834
|
+
let feedTypes;
|
|
8835
|
+
try {
|
|
8836
|
+
const body = yield c.req.json();
|
|
8837
|
+
feedTypes = body.feedTypes;
|
|
8838
|
+
} catch (e) {
|
|
8839
|
+
}
|
|
8840
|
+
if (!feedTypes || feedTypes.length === 0) {
|
|
8841
|
+
const allConfigs = this.container.config.EntityConfig;
|
|
8842
|
+
const visited = /* @__PURE__ */ new Set();
|
|
8843
|
+
const queue = [entityType];
|
|
8844
|
+
while (queue.length > 0) {
|
|
8845
|
+
const current = queue.shift();
|
|
8846
|
+
if (!current) continue;
|
|
8847
|
+
if (visited.has(current)) continue;
|
|
8848
|
+
visited.add(current);
|
|
8849
|
+
const config = allConfigs[current];
|
|
8850
|
+
if ((_a = config == null ? void 0 : config.mutual) == null ? void 0 : _a.mutualFields) {
|
|
8851
|
+
for (const field of Object.values(
|
|
8852
|
+
config.mutual.mutualFields
|
|
8853
|
+
)) {
|
|
8854
|
+
if (!visited.has(field.entityType)) {
|
|
8855
|
+
queue.push(field.entityType);
|
|
8856
|
+
}
|
|
8857
|
+
}
|
|
8858
|
+
}
|
|
8859
|
+
}
|
|
8860
|
+
visited.delete(entityType);
|
|
8861
|
+
feedTypes = Array.from(visited);
|
|
8862
|
+
}
|
|
8863
|
+
const ticket = ulid5();
|
|
8864
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
8865
|
+
const expiresAt = now + TICKET_TTL_SECONDS;
|
|
8866
|
+
yield this.container.websocketRepository.createTicket(
|
|
8867
|
+
ticket,
|
|
8868
|
+
entityType,
|
|
8869
|
+
entityId,
|
|
8870
|
+
feedTypes,
|
|
8871
|
+
expiresAt
|
|
8872
|
+
);
|
|
8873
|
+
const wsEndpoint = process.env.WEBSOCKET_URL || "";
|
|
8874
|
+
return c.json({
|
|
8875
|
+
ticket,
|
|
8876
|
+
wsUrl: wsEndpoint,
|
|
8877
|
+
expiresIn: TICKET_TTL_SECONDS
|
|
8878
|
+
});
|
|
8879
|
+
}));
|
|
8880
|
+
}
|
|
8881
|
+
};
|
|
8882
|
+
|
|
7970
8883
|
// services/entity-service-lifecycle.ts
|
|
7971
8884
|
var EntityServiceLifeCycle = class {
|
|
7972
8885
|
constructor(EntityConfig, publishEvent2, eventUtils) {
|
|
@@ -7999,6 +8912,435 @@ var EntityServiceLifeCycle = class {
|
|
|
7999
8912
|
}
|
|
8000
8913
|
};
|
|
8001
8914
|
|
|
8915
|
+
// services/transaction.service.ts
|
|
8916
|
+
import { TransactionCanceledException as TransactionCanceledException5 } from "@aws-sdk/client-dynamodb";
|
|
8917
|
+
import { ulid as ulid6 } from "ulid";
|
|
8918
|
+
var MAX_TRANSACTION_ITEMS = 100;
|
|
8919
|
+
var TransactionService = class {
|
|
8920
|
+
constructor(EntityConfig, EmailAuthEnabledEntities, entityRepository, dynamodbClient, publishEvent2, entityServiceLifeCycle, eventUtils) {
|
|
8921
|
+
this.EntityConfig = EntityConfig;
|
|
8922
|
+
this.EmailAuthEnabledEntities = EmailAuthEnabledEntities;
|
|
8923
|
+
this.entityRepository = entityRepository;
|
|
8924
|
+
this.dynamodbClient = dynamodbClient;
|
|
8925
|
+
this.publishEvent = publishEvent2;
|
|
8926
|
+
this.entityServiceLifeCycle = entityServiceLifeCycle;
|
|
8927
|
+
this.eventUtils = eventUtils;
|
|
8928
|
+
this.executeTransaction = (operations, accountId) => __async(this, null, function* () {
|
|
8929
|
+
var _a;
|
|
8930
|
+
if (!operations || operations.length === 0) {
|
|
8931
|
+
throw new StandardError(
|
|
8932
|
+
StandardErrorCode.TRANSACTION_EMPTY,
|
|
8933
|
+
"Transaction must contain at least one operation"
|
|
8934
|
+
);
|
|
8935
|
+
}
|
|
8936
|
+
const allTransactItems = [];
|
|
8937
|
+
const pendingEvents = [];
|
|
8938
|
+
const resultEntries = [];
|
|
8939
|
+
for (const op of operations) {
|
|
8940
|
+
switch (op.operation) {
|
|
8941
|
+
case "createEntity": {
|
|
8942
|
+
const { items, entity } = yield this.buildCreateItems(op);
|
|
8943
|
+
allTransactItems.push(...items);
|
|
8944
|
+
pendingEvents.push(
|
|
8945
|
+
...this.collectCreateEvents(
|
|
8946
|
+
entity,
|
|
8947
|
+
op.payload,
|
|
8948
|
+
accountId
|
|
8949
|
+
)
|
|
8950
|
+
);
|
|
8951
|
+
resultEntries.push({
|
|
8952
|
+
operation: "createEntity",
|
|
8953
|
+
entityType: op.entityType,
|
|
8954
|
+
entityId: entity.entityId,
|
|
8955
|
+
data: entity.data
|
|
8956
|
+
});
|
|
8957
|
+
break;
|
|
8958
|
+
}
|
|
8959
|
+
case "updateEntity": {
|
|
8960
|
+
const { item, updatedAt } = yield this.buildUpdateItem(op);
|
|
8961
|
+
allTransactItems.push(item);
|
|
8962
|
+
pendingEvents.push(
|
|
8963
|
+
...this.collectUpdateEvents(
|
|
8964
|
+
op,
|
|
8965
|
+
updatedAt,
|
|
8966
|
+
accountId
|
|
8967
|
+
)
|
|
8968
|
+
);
|
|
8969
|
+
resultEntries.push({
|
|
8970
|
+
operation: "updateEntity",
|
|
8971
|
+
entityType: op.entityType,
|
|
8972
|
+
entityId: op.entityId
|
|
8973
|
+
});
|
|
8974
|
+
break;
|
|
8975
|
+
}
|
|
8976
|
+
case "adjustEntity": {
|
|
8977
|
+
const { item, updatedAt } = yield this.buildAdjustItem(op);
|
|
8978
|
+
allTransactItems.push(item);
|
|
8979
|
+
pendingEvents.push({
|
|
8980
|
+
event: EVENT.CORE.ENTITY_UPDATED,
|
|
8981
|
+
payload: {
|
|
8982
|
+
entityType: op.entityType,
|
|
8983
|
+
entityId: op.entityId,
|
|
8984
|
+
updatedByAccountId: accountId,
|
|
8985
|
+
publishedAt: updatedAt
|
|
8986
|
+
}
|
|
8987
|
+
});
|
|
8988
|
+
resultEntries.push({
|
|
8989
|
+
operation: "adjustEntity",
|
|
8990
|
+
entityType: op.entityType,
|
|
8991
|
+
entityId: op.entityId
|
|
8992
|
+
});
|
|
8993
|
+
break;
|
|
8994
|
+
}
|
|
8995
|
+
case "deleteEntity": {
|
|
8996
|
+
const item = this.buildDeleteItem(op);
|
|
8997
|
+
allTransactItems.push(item);
|
|
8998
|
+
pendingEvents.push({
|
|
8999
|
+
event: EVENT.CORE.ENTITY_DELETED,
|
|
9000
|
+
payload: {
|
|
9001
|
+
entityType: op.entityType,
|
|
9002
|
+
entityId: op.entityId,
|
|
9003
|
+
deletedByAccountId: accountId
|
|
9004
|
+
}
|
|
9005
|
+
});
|
|
9006
|
+
resultEntries.push({
|
|
9007
|
+
operation: "deleteEntity",
|
|
9008
|
+
entityType: op.entityType,
|
|
9009
|
+
entityId: op.entityId
|
|
9010
|
+
});
|
|
9011
|
+
break;
|
|
9012
|
+
}
|
|
9013
|
+
default:
|
|
9014
|
+
throw new StandardError(
|
|
9015
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9016
|
+
`Unknown operation: '${op.operation}'`
|
|
9017
|
+
);
|
|
9018
|
+
}
|
|
9019
|
+
}
|
|
9020
|
+
if (allTransactItems.length > MAX_TRANSACTION_ITEMS) {
|
|
9021
|
+
throw new StandardError(
|
|
9022
|
+
StandardErrorCode.TRANSACTION_ITEM_LIMIT_EXCEEDED,
|
|
9023
|
+
`Transaction contains ${allTransactItems.length} items, exceeds limit of ${MAX_TRANSACTION_ITEMS}`
|
|
9024
|
+
);
|
|
9025
|
+
}
|
|
9026
|
+
try {
|
|
9027
|
+
yield this.dynamodbClient.transactWriteItems({
|
|
9028
|
+
TransactItems: allTransactItems
|
|
9029
|
+
});
|
|
9030
|
+
} catch (err) {
|
|
9031
|
+
if (err instanceof TransactionCanceledException5) {
|
|
9032
|
+
throw new StandardError(
|
|
9033
|
+
StandardErrorCode.TRANSACTION_FAILED,
|
|
9034
|
+
"Transaction failed",
|
|
9035
|
+
err,
|
|
9036
|
+
{
|
|
9037
|
+
reasons: (_a = err.CancellationReasons) == null ? void 0 : _a.map((r, i) => ({
|
|
9038
|
+
index: i,
|
|
9039
|
+
code: r.Code,
|
|
9040
|
+
message: r.Message
|
|
9041
|
+
}))
|
|
9042
|
+
}
|
|
9043
|
+
);
|
|
9044
|
+
}
|
|
9045
|
+
throw err;
|
|
9046
|
+
}
|
|
9047
|
+
const readPromises = resultEntries.map((entry) => __async(this, null, function* () {
|
|
9048
|
+
if ((entry.operation === "updateEntity" || entry.operation === "adjustEntity") && !entry.data) {
|
|
9049
|
+
try {
|
|
9050
|
+
const entity = yield this.entityRepository.getEntity(
|
|
9051
|
+
entry.entityType,
|
|
9052
|
+
entry.entityId
|
|
9053
|
+
);
|
|
9054
|
+
entry.data = entity.data;
|
|
9055
|
+
} catch (e) {
|
|
9056
|
+
}
|
|
9057
|
+
}
|
|
9058
|
+
}));
|
|
9059
|
+
yield Promise.all(readPromises);
|
|
9060
|
+
yield Promise.allSettled(pendingEvents.map((ev) => this.publishEvent(ev)));
|
|
9061
|
+
return { results: resultEntries };
|
|
9062
|
+
});
|
|
9063
|
+
}
|
|
9064
|
+
buildCreateItems(op) {
|
|
9065
|
+
return __async(this, null, function* () {
|
|
9066
|
+
const config = this.EntityConfig[op.entityType];
|
|
9067
|
+
if (!config) {
|
|
9068
|
+
throw new StandardError(
|
|
9069
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9070
|
+
`Unknown entity type: '${op.entityType}'`
|
|
9071
|
+
);
|
|
9072
|
+
}
|
|
9073
|
+
const entitySchema = config.createSchema || config.baseSchema;
|
|
9074
|
+
if (!entitySchema) {
|
|
9075
|
+
throw new StandardError(
|
|
9076
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9077
|
+
`No schema defined for entity type: '${op.entityType}'`
|
|
9078
|
+
);
|
|
9079
|
+
}
|
|
9080
|
+
if (config.finalSchema) {
|
|
9081
|
+
config.finalSchema.parse(op.payload);
|
|
9082
|
+
}
|
|
9083
|
+
const parsedPayload = entitySchema.parse(
|
|
9084
|
+
op.payload
|
|
9085
|
+
);
|
|
9086
|
+
const currentDatetime = /* @__PURE__ */ new Date();
|
|
9087
|
+
const entity = new Entity(
|
|
9088
|
+
op.entityType,
|
|
9089
|
+
op.entityId || ulid6(),
|
|
9090
|
+
parsedPayload,
|
|
9091
|
+
currentDatetime,
|
|
9092
|
+
currentDatetime
|
|
9093
|
+
);
|
|
9094
|
+
const uniqueFields = config.uniqueFields || [];
|
|
9095
|
+
const uniqueFieldValues = {};
|
|
9096
|
+
for (const field of uniqueFields) {
|
|
9097
|
+
if (!(field in parsedPayload)) continue;
|
|
9098
|
+
const value = parsedPayload[field];
|
|
9099
|
+
if (typeof value !== "string") {
|
|
9100
|
+
throw new StandardError(
|
|
9101
|
+
StandardErrorCode.INVALID_UNIQUE_VALUE_TYPE,
|
|
9102
|
+
`Invalid type. ${field} is not a 'string'.`
|
|
9103
|
+
);
|
|
9104
|
+
}
|
|
9105
|
+
uniqueFieldValues[field] = value;
|
|
9106
|
+
}
|
|
9107
|
+
const items = this.entityRepository.createEntityTransactItems(entity, {
|
|
9108
|
+
uniqueFieldValues
|
|
9109
|
+
});
|
|
9110
|
+
return { items, entity };
|
|
9111
|
+
});
|
|
9112
|
+
}
|
|
9113
|
+
buildUpdateItem(op) {
|
|
9114
|
+
return __async(this, null, function* () {
|
|
9115
|
+
const config = this.EntityConfig[op.entityType];
|
|
9116
|
+
if (!config) {
|
|
9117
|
+
throw new StandardError(
|
|
9118
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9119
|
+
`Unknown entity type: '${op.entityType}'`
|
|
9120
|
+
);
|
|
9121
|
+
}
|
|
9122
|
+
const uniqueFields = config.uniqueFields || [];
|
|
9123
|
+
for (const field of uniqueFields) {
|
|
9124
|
+
if (field in op.payload) {
|
|
9125
|
+
throw new StandardError(
|
|
9126
|
+
StandardErrorCode.TRANSACTION_UNIQUE_FIELD_UPDATE,
|
|
9127
|
+
`Cannot update unique field '${field}' within a transaction. Use a standalone updateEntity call instead.`
|
|
9128
|
+
);
|
|
9129
|
+
}
|
|
9130
|
+
}
|
|
9131
|
+
const entitySchema = config.baseSchema;
|
|
9132
|
+
if (!entitySchema) {
|
|
9133
|
+
throw new StandardError(
|
|
9134
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9135
|
+
`No schema defined for entity type: '${op.entityType}'`
|
|
9136
|
+
);
|
|
9137
|
+
}
|
|
9138
|
+
const parsedPayload = entitySchema.partial().parse(op.payload);
|
|
9139
|
+
const currentDatetime = (/* @__PURE__ */ new Date()).toISOString();
|
|
9140
|
+
const toUpdateExpressions = this.entityRepository.toUpdate({
|
|
9141
|
+
updatedAt: currentDatetime,
|
|
9142
|
+
data: parsedPayload
|
|
9143
|
+
});
|
|
9144
|
+
let conditionOpts;
|
|
9145
|
+
if (op.condition) {
|
|
9146
|
+
const updateConditions = config.updateConditions;
|
|
9147
|
+
if (!updateConditions) {
|
|
9148
|
+
throw new StandardError(
|
|
9149
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
9150
|
+
`Entity '${op.entityType}' has no updateConditions defined`
|
|
9151
|
+
);
|
|
9152
|
+
}
|
|
9153
|
+
conditionOpts = yield resolveUpdateCondition({
|
|
9154
|
+
conditionName: op.condition,
|
|
9155
|
+
conditions: updateConditions,
|
|
9156
|
+
getEntityData: () => __async(this, null, function* () {
|
|
9157
|
+
var _a;
|
|
9158
|
+
const entity2 = yield this.entityRepository.getEntity(
|
|
9159
|
+
op.entityType,
|
|
9160
|
+
op.entityId
|
|
9161
|
+
);
|
|
9162
|
+
return (_a = entity2 == null ? void 0 : entity2.data) != null ? _a : {};
|
|
9163
|
+
})
|
|
9164
|
+
});
|
|
9165
|
+
}
|
|
9166
|
+
const entity = new Entity(op.entityType, op.entityId);
|
|
9167
|
+
const item = {
|
|
9168
|
+
Update: {
|
|
9169
|
+
TableName: this.entityRepository.TABLE_NAME,
|
|
9170
|
+
Key: entity.keys(),
|
|
9171
|
+
ConditionExpression: (conditionOpts == null ? void 0 : conditionOpts.ConditionExpression) || "attribute_exists(PK)",
|
|
9172
|
+
UpdateExpression: toUpdateExpressions.UpdateExpression,
|
|
9173
|
+
ExpressionAttributeNames: __spreadValues(__spreadValues({}, toUpdateExpressions.ExpressionAttributeNames), conditionOpts == null ? void 0 : conditionOpts.ExpressionAttributeNames),
|
|
9174
|
+
ExpressionAttributeValues: __spreadValues(__spreadValues({}, toUpdateExpressions.ExpressionAttributeValues), conditionOpts == null ? void 0 : conditionOpts.ExpressionAttributeValues)
|
|
9175
|
+
}
|
|
9176
|
+
};
|
|
9177
|
+
return { item, updatedAt: currentDatetime };
|
|
9178
|
+
});
|
|
9179
|
+
}
|
|
9180
|
+
buildAdjustItem(op) {
|
|
9181
|
+
return __async(this, null, function* () {
|
|
9182
|
+
const config = this.EntityConfig[op.entityType];
|
|
9183
|
+
if (!config) {
|
|
9184
|
+
throw new StandardError(
|
|
9185
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9186
|
+
`Unknown entity type: '${op.entityType}'`
|
|
9187
|
+
);
|
|
9188
|
+
}
|
|
9189
|
+
for (const [key, value] of Object.entries(op.adjustments)) {
|
|
9190
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
9191
|
+
throw new StandardError(
|
|
9192
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9193
|
+
`Adjustment field "${key}" must be a finite number`
|
|
9194
|
+
);
|
|
9195
|
+
}
|
|
9196
|
+
}
|
|
9197
|
+
const {
|
|
9198
|
+
UpdateExpression,
|
|
9199
|
+
ExpressionAttributeNames,
|
|
9200
|
+
ExpressionAttributeValues
|
|
9201
|
+
} = this.entityRepository.toAdjustUpdate(op.adjustments);
|
|
9202
|
+
const currentDatetime = (/* @__PURE__ */ new Date()).toISOString();
|
|
9203
|
+
ExpressionAttributeNames["#updatedAt"] = "updatedAt";
|
|
9204
|
+
ExpressionAttributeValues[":updatedAt"] = { S: currentDatetime };
|
|
9205
|
+
const fullUpdateExpression = `${UpdateExpression}, #updatedAt = :updatedAt`;
|
|
9206
|
+
let conditionOpts;
|
|
9207
|
+
const adjustmentConditions = config.adjustmentConditions;
|
|
9208
|
+
if (adjustmentConditions) {
|
|
9209
|
+
if (!op.condition) {
|
|
9210
|
+
throw new StandardError(
|
|
9211
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
9212
|
+
`Entity '${op.entityType}' has adjustmentConditions defined; condition is required`
|
|
9213
|
+
);
|
|
9214
|
+
}
|
|
9215
|
+
conditionOpts = yield resolveAdjustmentCondition({
|
|
9216
|
+
conditionName: op.condition,
|
|
9217
|
+
conditions: adjustmentConditions,
|
|
9218
|
+
adjustments: op.adjustments,
|
|
9219
|
+
getEntityData: () => __async(this, null, function* () {
|
|
9220
|
+
var _a;
|
|
9221
|
+
const entity2 = yield this.entityRepository.getEntity(
|
|
9222
|
+
op.entityType,
|
|
9223
|
+
op.entityId
|
|
9224
|
+
);
|
|
9225
|
+
return (_a = entity2 == null ? void 0 : entity2.data) != null ? _a : {};
|
|
9226
|
+
})
|
|
9227
|
+
});
|
|
9228
|
+
}
|
|
9229
|
+
const entity = new Entity(op.entityType, op.entityId);
|
|
9230
|
+
const item = {
|
|
9231
|
+
Update: {
|
|
9232
|
+
TableName: this.entityRepository.TABLE_NAME,
|
|
9233
|
+
Key: entity.keys(),
|
|
9234
|
+
UpdateExpression: fullUpdateExpression,
|
|
9235
|
+
ConditionExpression: (conditionOpts == null ? void 0 : conditionOpts.ConditionExpression) || "attribute_exists(PK)",
|
|
9236
|
+
ExpressionAttributeNames: __spreadValues(__spreadValues({}, ExpressionAttributeNames), conditionOpts == null ? void 0 : conditionOpts.ExpressionAttributeNames),
|
|
9237
|
+
ExpressionAttributeValues: __spreadValues(__spreadValues({}, ExpressionAttributeValues), conditionOpts == null ? void 0 : conditionOpts.ExpressionAttributeValues)
|
|
9238
|
+
}
|
|
9239
|
+
};
|
|
9240
|
+
return { item, updatedAt: currentDatetime };
|
|
9241
|
+
});
|
|
9242
|
+
}
|
|
9243
|
+
buildDeleteItem(op) {
|
|
9244
|
+
const config = this.EntityConfig[op.entityType];
|
|
9245
|
+
if (!config) {
|
|
9246
|
+
throw new StandardError(
|
|
9247
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9248
|
+
`Unknown entity type: '${op.entityType}'`
|
|
9249
|
+
);
|
|
9250
|
+
}
|
|
9251
|
+
const entity = new Entity(op.entityType, op.entityId);
|
|
9252
|
+
return {
|
|
9253
|
+
Delete: {
|
|
9254
|
+
TableName: this.entityRepository.TABLE_NAME,
|
|
9255
|
+
Key: entity.keys(),
|
|
9256
|
+
ConditionExpression: "attribute_exists(PK)"
|
|
9257
|
+
}
|
|
9258
|
+
};
|
|
9259
|
+
}
|
|
9260
|
+
collectCreateEvents(entity, payload, accountId) {
|
|
9261
|
+
var _a, _b;
|
|
9262
|
+
const events = [];
|
|
9263
|
+
const publishedAt = entity.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
9264
|
+
const config = this.EntityConfig[entity.entityType];
|
|
9265
|
+
const mutualSchema = (_a = config == null ? void 0 : config.mutual) == null ? void 0 : _a.mutualSchema;
|
|
9266
|
+
if (mutualSchema) {
|
|
9267
|
+
const parsedMutualPayload = mutualSchema.parse(payload);
|
|
9268
|
+
if (parsedMutualPayload) {
|
|
9269
|
+
for (const [fieldKey, fieldConfig] of Object.entries(
|
|
9270
|
+
((_b = config.mutual) == null ? void 0 : _b.mutualFields) || {}
|
|
9271
|
+
)) {
|
|
9272
|
+
const toMutualIds = fieldConfig.toMutualIds;
|
|
9273
|
+
const mutualPayload = parsedMutualPayload[fieldKey];
|
|
9274
|
+
if (!mutualPayload) continue;
|
|
9275
|
+
events.push({
|
|
9276
|
+
event: EVENT.CORE.ENTITY_MUTUAL_TO_CREATE,
|
|
9277
|
+
payload: {
|
|
9278
|
+
byEntityType: entity.entityType,
|
|
9279
|
+
byEntityId: entity.entityId,
|
|
9280
|
+
entityType: fieldConfig.entityType,
|
|
9281
|
+
field: fieldKey,
|
|
9282
|
+
mutualIds: toMutualIds ? toMutualIds(mutualPayload) : mutualPayload,
|
|
9283
|
+
customContext: toMutualIds ? mutualPayload : {},
|
|
9284
|
+
publishedAt
|
|
9285
|
+
}
|
|
9286
|
+
});
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
events.push({
|
|
9291
|
+
event: EVENT.CORE.ENTITY_CREATED,
|
|
9292
|
+
payload: {
|
|
9293
|
+
entityType: entity.entityType,
|
|
9294
|
+
entityId: entity.entityId,
|
|
9295
|
+
data: entity.data,
|
|
9296
|
+
createdByAccountId: accountId,
|
|
9297
|
+
publishedAt
|
|
9298
|
+
}
|
|
9299
|
+
});
|
|
9300
|
+
return events;
|
|
9301
|
+
}
|
|
9302
|
+
collectUpdateEvents(op, updatedAt, accountId) {
|
|
9303
|
+
var _a, _b;
|
|
9304
|
+
const events = [];
|
|
9305
|
+
const config = this.EntityConfig[op.entityType];
|
|
9306
|
+
const mutualSchema = (_a = config == null ? void 0 : config.mutual) == null ? void 0 : _a.mutualSchema;
|
|
9307
|
+
if (mutualSchema) {
|
|
9308
|
+
const parsedMutualPayload = mutualSchema.parse(op.payload);
|
|
9309
|
+
if (parsedMutualPayload) {
|
|
9310
|
+
for (const [fieldKey, fieldConfig] of Object.entries(
|
|
9311
|
+
((_b = config.mutual) == null ? void 0 : _b.mutualFields) || {}
|
|
9312
|
+
)) {
|
|
9313
|
+
const toMutualIds = fieldConfig.toMutualIds;
|
|
9314
|
+
const mutualPayload = parsedMutualPayload[fieldKey];
|
|
9315
|
+
if (!mutualPayload) continue;
|
|
9316
|
+
events.push({
|
|
9317
|
+
event: EVENT.CORE.ENTITY_MUTUAL_TO_UPDATE,
|
|
9318
|
+
payload: {
|
|
9319
|
+
byEntityType: op.entityType,
|
|
9320
|
+
byEntityId: op.entityId,
|
|
9321
|
+
entityType: fieldConfig.entityType,
|
|
9322
|
+
field: fieldKey,
|
|
9323
|
+
mutualIds: toMutualIds ? toMutualIds(mutualPayload) : mutualPayload,
|
|
9324
|
+
customContext: toMutualIds ? mutualPayload : {},
|
|
9325
|
+
publishedAt: updatedAt
|
|
9326
|
+
}
|
|
9327
|
+
});
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
}
|
|
9331
|
+
events.push({
|
|
9332
|
+
event: EVENT.CORE.ENTITY_UPDATED,
|
|
9333
|
+
payload: {
|
|
9334
|
+
entityType: op.entityType,
|
|
9335
|
+
entityId: op.entityId,
|
|
9336
|
+
updatedByAccountId: accountId,
|
|
9337
|
+
publishedAt: updatedAt
|
|
9338
|
+
}
|
|
9339
|
+
});
|
|
9340
|
+
return events;
|
|
9341
|
+
}
|
|
9342
|
+
};
|
|
9343
|
+
|
|
8002
9344
|
// services/DependencyContainer.ts
|
|
8003
9345
|
var DependencyContainer = class {
|
|
8004
9346
|
constructor(config) {
|
|
@@ -8077,6 +9419,7 @@ var DependencyContainer = class {
|
|
|
8077
9419
|
get mutualService() {
|
|
8078
9420
|
return this.createCachedInstance(
|
|
8079
9421
|
MutualService,
|
|
9422
|
+
this.config.EntityConfig,
|
|
8080
9423
|
this.entityRepository,
|
|
8081
9424
|
this.mutualRepository,
|
|
8082
9425
|
this.publishEvent,
|
|
@@ -8091,6 +9434,13 @@ var DependencyContainer = class {
|
|
|
8091
9434
|
this.dynamodbClient
|
|
8092
9435
|
);
|
|
8093
9436
|
}
|
|
9437
|
+
get websocketRepository() {
|
|
9438
|
+
return this.createCachedInstance(
|
|
9439
|
+
WebSocketRepository,
|
|
9440
|
+
this.coreTable,
|
|
9441
|
+
this.dynamodbClient
|
|
9442
|
+
);
|
|
9443
|
+
}
|
|
8094
9444
|
get getEntityController() {
|
|
8095
9445
|
return this.createCachedInstance(
|
|
8096
9446
|
GetEntityController,
|
|
@@ -8174,6 +9524,62 @@ var DependencyContainer = class {
|
|
|
8174
9524
|
get listTagsController() {
|
|
8175
9525
|
return this.createCachedInstance(ListTagsController, this.tagRepository);
|
|
8176
9526
|
}
|
|
9527
|
+
get transactionService() {
|
|
9528
|
+
return this.createCachedInstance(
|
|
9529
|
+
TransactionService,
|
|
9530
|
+
this.config.EntityConfig,
|
|
9531
|
+
this.config.EmailAuthEnabledEntities,
|
|
9532
|
+
this.entityRepository,
|
|
9533
|
+
this.dynamodbClient,
|
|
9534
|
+
this.publishEvent,
|
|
9535
|
+
this.entityServiceLifeCycle,
|
|
9536
|
+
this.eventUtils
|
|
9537
|
+
);
|
|
9538
|
+
}
|
|
9539
|
+
get executeTransactionController() {
|
|
9540
|
+
return this.createCachedInstance(
|
|
9541
|
+
ExecuteTransactionController,
|
|
9542
|
+
this.transactionService
|
|
9543
|
+
);
|
|
9544
|
+
}
|
|
9545
|
+
get createTicketController() {
|
|
9546
|
+
return this.createCachedInstance(CreateTicketController, this);
|
|
9547
|
+
}
|
|
9548
|
+
};
|
|
9549
|
+
|
|
9550
|
+
// helpers/transactional.ts
|
|
9551
|
+
var transactional = {
|
|
9552
|
+
createEntity: (entityType, payload) => {
|
|
9553
|
+
const _a = payload, { entityId } = _a, rest = __objRest(_a, ["entityId"]);
|
|
9554
|
+
return __spreadValues({
|
|
9555
|
+
operation: "createEntity",
|
|
9556
|
+
entityType,
|
|
9557
|
+
payload: rest
|
|
9558
|
+
}, entityId && { entityId });
|
|
9559
|
+
},
|
|
9560
|
+
updateEntity: (entityType, entityId, payload) => {
|
|
9561
|
+
const _a = payload, { $condition } = _a, rest = __objRest(_a, ["$condition"]);
|
|
9562
|
+
return __spreadValues({
|
|
9563
|
+
operation: "updateEntity",
|
|
9564
|
+
entityType,
|
|
9565
|
+
entityId,
|
|
9566
|
+
payload: rest
|
|
9567
|
+
}, $condition && { condition: $condition });
|
|
9568
|
+
},
|
|
9569
|
+
adjustEntity: (entityType, entityId, adjustments) => {
|
|
9570
|
+
const _a = adjustments, { $condition } = _a, rest = __objRest(_a, ["$condition"]);
|
|
9571
|
+
return __spreadValues({
|
|
9572
|
+
operation: "adjustEntity",
|
|
9573
|
+
entityType,
|
|
9574
|
+
entityId,
|
|
9575
|
+
adjustments: rest
|
|
9576
|
+
}, $condition && { condition: $condition });
|
|
9577
|
+
},
|
|
9578
|
+
deleteEntity: (entityType, entityId) => ({
|
|
9579
|
+
operation: "deleteEntity",
|
|
9580
|
+
entityType,
|
|
9581
|
+
entityId
|
|
9582
|
+
})
|
|
8177
9583
|
};
|
|
8178
9584
|
|
|
8179
9585
|
// index.ts
|
|
@@ -8189,6 +9595,10 @@ var CoreFactory = class {
|
|
|
8189
9595
|
this.prejoinProcessor = handler3(dependencyContainer);
|
|
8190
9596
|
this.tagProcessor = handler5(dependencyContainer);
|
|
8191
9597
|
this.appHandler = appHandler(dependencyContainer);
|
|
9598
|
+
this.wsConnect = connect(dependencyContainer);
|
|
9599
|
+
this.wsDisconnect = disconnect(dependencyContainer);
|
|
9600
|
+
this.wsDefault = $default(dependencyContainer);
|
|
9601
|
+
this.wsBroadcast = broadcast(dependencyContainer);
|
|
8192
9602
|
}
|
|
8193
9603
|
};
|
|
8194
9604
|
var index_default = CoreFactory;
|
|
@@ -8204,6 +9614,8 @@ export {
|
|
|
8204
9614
|
StandardError,
|
|
8205
9615
|
StandardErrorCode,
|
|
8206
9616
|
TagRepository,
|
|
9617
|
+
TransactionService,
|
|
9618
|
+
WebSocketRepository,
|
|
8207
9619
|
appHandler,
|
|
8208
9620
|
handler as createEntityProcessor,
|
|
8209
9621
|
index_default as default,
|
|
@@ -8211,6 +9623,11 @@ export {
|
|
|
8211
9623
|
handler3 as prejoinProcessor,
|
|
8212
9624
|
handler4 as replicationProcessor,
|
|
8213
9625
|
setupCommonRoutes,
|
|
8214
|
-
handler5 as tagProcessor
|
|
9626
|
+
handler5 as tagProcessor,
|
|
9627
|
+
transactional,
|
|
9628
|
+
broadcast as wsBroadcast,
|
|
9629
|
+
connect as wsConnect,
|
|
9630
|
+
$default as wsDefault,
|
|
9631
|
+
disconnect as wsDisconnect
|
|
8215
9632
|
};
|
|
8216
9633
|
//# sourceMappingURL=index.js.map
|