monorise 1.0.0 → 1.1.0-dev.0
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 +84 -3
- 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 +182 -35
- package/dist/core/index.js +1486 -94
- 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-4D22OCZG.js +65 -0
- package/dist/react/chunk-4D22OCZG.js.map +1 -0
- package/dist/react/chunk-4N3P4ONH.js +588 -0
- package/dist/react/chunk-4N3P4ONH.js.map +1 -0
- package/dist/react/chunk-4Y4KWGJD.js +182 -0
- package/dist/react/chunk-4Y4KWGJD.js.map +1 -0
- package/dist/react/chunk-757E5UYA.js +893 -0
- package/dist/react/chunk-757E5UYA.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-B3XDGUFO.js +489 -0
- package/dist/react/chunk-B3XDGUFO.js.map +1 -0
- package/dist/react/chunk-BPBCUO2Z.js +248 -0
- package/dist/react/chunk-BPBCUO2Z.js.map +1 -0
- package/dist/react/chunk-CQBOIXWK.js +142 -0
- package/dist/react/chunk-CQBOIXWK.js.map +1 -0
- package/dist/react/chunk-DRH2BB7I.js +383 -0
- package/dist/react/chunk-DRH2BB7I.js.map +1 -0
- package/dist/react/chunk-EQ3PKQ2S.js +402 -0
- package/dist/react/chunk-EQ3PKQ2S.js.map +1 -0
- package/dist/react/chunk-H64MMAL7.js +245 -0
- package/dist/react/chunk-H64MMAL7.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-MO35V2Y7.js +172 -0
- package/dist/react/chunk-MO35V2Y7.js.map +1 -0
- package/dist/react/chunk-UC3E72G7.js +73 -0
- package/dist/react/chunk-UC3E72G7.js.map +1 -0
- package/dist/react/chunk-UHMKB3OR.js +5568 -0
- package/dist/react/chunk-UHMKB3OR.js.map +1 -0
- package/dist/react/chunk-UQPQBWEQ.js +54 -0
- package/dist/react/chunk-UQPQBWEQ.js.map +1 -0
- package/dist/react/chunk-XCDCVRJR.js +43 -0
- package/dist/react/chunk-XCDCVRJR.js.map +1 -0
- package/dist/react/chunk-XOYAZDIH.js +47 -0
- package/dist/react/chunk-XOYAZDIH.js.map +1 -0
- package/dist/react/chunk-YNFQEPO5.js +29 -0
- package/dist/react/chunk-YNFQEPO5.js.map +1 -0
- package/dist/react/dist-es-35AO47NO.js +90 -0
- package/dist/react/dist-es-35AO47NO.js.map +1 -0
- package/dist/react/dist-es-5GDBXNKQ.js +333 -0
- package/dist/react/dist-es-5GDBXNKQ.js.map +1 -0
- package/dist/react/dist-es-B3JDGWY6.js +71 -0
- package/dist/react/dist-es-B3JDGWY6.js.map +1 -0
- package/dist/react/dist-es-IWIE5JLA.js +169 -0
- package/dist/react/dist-es-IWIE5JLA.js.map +1 -0
- package/dist/react/dist-es-NRIS3TYJ.js +494 -0
- package/dist/react/dist-es-NRIS3TYJ.js.map +1 -0
- package/dist/react/dist-es-VCXAEYYN.js +22 -0
- package/dist/react/dist-es-VCXAEYYN.js.map +1 -0
- package/dist/react/dist-es-VU33JFTZ.js +379 -0
- package/dist/react/dist-es-VU33JFTZ.js.map +1 -0
- package/dist/react/event-streams-OSOTOTTP.js +277 -0
- package/dist/react/event-streams-OSOTOTTP.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 +10948 -190
- package/dist/react/index.js.map +1 -1
- package/dist/react/loadSso-ME7MKAM3.js +556 -0
- package/dist/react/loadSso-ME7MKAM3.js.map +1 -0
- package/dist/react/service.config-ZJEZ6EKA-FC2TR3GH.js +14 -0
- package/dist/react/service.config-ZJEZ6EKA-FC2TR3GH.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-LOXYIE5I.js +653 -0
- package/dist/react/signin-LOXYIE5I.js.map +1 -0
- package/dist/react/sso-oidc-X63KRRLO.js +786 -0
- package/dist/react/sso-oidc-X63KRRLO.js.map +1 -0
- package/dist/react/sts-OXBEY7HY.js +3948 -0
- package/dist/react/sts-OXBEY7HY.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-QHA7SQXG.js +10 -0
- package/dist/react/websocket-QHA7SQXG.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",
|
|
@@ -2855,6 +3023,458 @@ var handler5 = (container) => (ev) => __async(null, null, function* () {
|
|
|
2855
3023
|
return { batchItemFailures };
|
|
2856
3024
|
});
|
|
2857
3025
|
|
|
3026
|
+
// processors/websocket-processor.ts
|
|
3027
|
+
import {
|
|
3028
|
+
ApiGatewayManagementApiClient,
|
|
3029
|
+
PostToConnectionCommand
|
|
3030
|
+
} from "@aws-sdk/client-apigatewaymanagementapi";
|
|
3031
|
+
import { unmarshall as unmarshall4 } from "@aws-sdk/util-dynamodb";
|
|
3032
|
+
import { ulid as ulid3 } from "ulid";
|
|
3033
|
+
var SUB_ENTITY_TYPE = "SUB#ENTITY#";
|
|
3034
|
+
var SUB_MUTUAL_TYPE = "SUB#MUTUAL#";
|
|
3035
|
+
var SUB_EPHEMERAL = "SUB#EPHEMERAL#";
|
|
3036
|
+
var SUB_FEED = "SUB#FEED#";
|
|
3037
|
+
var getWsEndpoint = () => process.env.WEBSOCKET_MANAGEMENT_ENDPOINT || "";
|
|
3038
|
+
var connect = (container) => (event) => __async(null, null, function* () {
|
|
3039
|
+
var _a, _b, _c, _d;
|
|
3040
|
+
const connectionId = event.requestContext.connectionId;
|
|
3041
|
+
if (!connectionId) {
|
|
3042
|
+
return { statusCode: 400, body: "Missing connection ID" };
|
|
3043
|
+
}
|
|
3044
|
+
const expiresAt = Math.floor(Date.now() / 1e3) + 2 * 60 * 60;
|
|
3045
|
+
const ticket = (_a = event.queryStringParameters) == null ? void 0 : _a.ticket;
|
|
3046
|
+
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);
|
|
3047
|
+
if (!ticket && !token) {
|
|
3048
|
+
return { statusCode: 401, body: "Unauthorized" };
|
|
3049
|
+
}
|
|
3050
|
+
const wsRepo = container.websocketRepository;
|
|
3051
|
+
try {
|
|
3052
|
+
let entityType;
|
|
3053
|
+
let entityId;
|
|
3054
|
+
let feedTypes;
|
|
3055
|
+
if (ticket) {
|
|
3056
|
+
const ticketData = yield wsRepo.consumeTicket(ticket);
|
|
3057
|
+
if (!ticketData) {
|
|
3058
|
+
return { statusCode: 401, body: "Invalid or expired ticket" };
|
|
3059
|
+
}
|
|
3060
|
+
entityType = ticketData.entityType;
|
|
3061
|
+
entityId = ticketData.entityId;
|
|
3062
|
+
feedTypes = ticketData.feedTypes;
|
|
3063
|
+
} else {
|
|
3064
|
+
entityId = token;
|
|
3065
|
+
}
|
|
3066
|
+
yield wsRepo.createConnection(
|
|
3067
|
+
connectionId,
|
|
3068
|
+
__spreadProps(__spreadValues(__spreadValues({}, entityType && { entityType }), entityId && { entityId }), {
|
|
3069
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3070
|
+
}),
|
|
3071
|
+
expiresAt
|
|
3072
|
+
);
|
|
3073
|
+
if (entityType && entityId && feedTypes) {
|
|
3074
|
+
yield wsRepo.createSubscription(
|
|
3075
|
+
`${SUB_FEED}${entityType}#${entityId}`,
|
|
3076
|
+
connectionId,
|
|
3077
|
+
{
|
|
3078
|
+
subscriptionType: "feed",
|
|
3079
|
+
entityType,
|
|
3080
|
+
entityId,
|
|
3081
|
+
feedTypes,
|
|
3082
|
+
subscribedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3083
|
+
expiresAt
|
|
3084
|
+
}
|
|
3085
|
+
);
|
|
3086
|
+
}
|
|
3087
|
+
return { statusCode: 200, body: "Connected" };
|
|
3088
|
+
} catch (error) {
|
|
3089
|
+
console.error("Error in $connect:", error);
|
|
3090
|
+
return { statusCode: 500, body: "Failed to connect" };
|
|
3091
|
+
}
|
|
3092
|
+
});
|
|
3093
|
+
var disconnect = (container) => (event) => __async(null, null, function* () {
|
|
3094
|
+
const connectionId = event.requestContext.connectionId;
|
|
3095
|
+
if (!connectionId) {
|
|
3096
|
+
return { statusCode: 400, body: "Missing connection ID" };
|
|
3097
|
+
}
|
|
3098
|
+
const wsRepo = container.websocketRepository;
|
|
3099
|
+
try {
|
|
3100
|
+
const subscriptions = yield wsRepo.querySubscriptionsByConnectionId(connectionId);
|
|
3101
|
+
const deletePromises = [];
|
|
3102
|
+
for (const item of subscriptions) {
|
|
3103
|
+
if (item.PK && item.SK) {
|
|
3104
|
+
deletePromises.push(
|
|
3105
|
+
wsRepo.deleteSubscription(item.PK, item.connectionId).catch(
|
|
3106
|
+
(e) => console.warn("Failed to delete subscription on disconnect:", e)
|
|
3107
|
+
)
|
|
3108
|
+
);
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
deletePromises.push(wsRepo.deleteConnection(connectionId));
|
|
3112
|
+
yield Promise.all(deletePromises);
|
|
3113
|
+
return { statusCode: 200, body: "Disconnected" };
|
|
3114
|
+
} catch (error) {
|
|
3115
|
+
console.error("Error cleaning up connection:", error);
|
|
3116
|
+
return { statusCode: 500, body: "Failed to disconnect" };
|
|
3117
|
+
}
|
|
3118
|
+
});
|
|
3119
|
+
var $default = (container) => (event) => __async(null, null, function* () {
|
|
3120
|
+
const connectionId = event.requestContext.connectionId;
|
|
3121
|
+
if (!connectionId || !event.body) {
|
|
3122
|
+
return { statusCode: 400, body: "Invalid message" };
|
|
3123
|
+
}
|
|
3124
|
+
let message;
|
|
3125
|
+
try {
|
|
3126
|
+
message = JSON.parse(event.body);
|
|
3127
|
+
} catch (e) {
|
|
3128
|
+
return { statusCode: 400, body: "Invalid JSON" };
|
|
3129
|
+
}
|
|
3130
|
+
const wsRepo = container.websocketRepository;
|
|
3131
|
+
const wsEndpoint = getWsEndpoint();
|
|
3132
|
+
const managementApi = new ApiGatewayManagementApiClient({
|
|
3133
|
+
endpoint: wsEndpoint
|
|
3134
|
+
});
|
|
3135
|
+
try {
|
|
3136
|
+
switch (message.action) {
|
|
3137
|
+
case "subscribe": {
|
|
3138
|
+
const {
|
|
3139
|
+
entityType,
|
|
3140
|
+
byEntityType,
|
|
3141
|
+
byEntityId,
|
|
3142
|
+
mutualEntityType,
|
|
3143
|
+
channel
|
|
3144
|
+
} = message.payload;
|
|
3145
|
+
if (entityType && !byEntityType) {
|
|
3146
|
+
yield wsRepo.createSubscription(
|
|
3147
|
+
`${SUB_ENTITY_TYPE}${entityType}`,
|
|
3148
|
+
connectionId,
|
|
3149
|
+
{
|
|
3150
|
+
subscriptionType: "entity-type",
|
|
3151
|
+
entityType,
|
|
3152
|
+
subscribedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3153
|
+
}
|
|
3154
|
+
);
|
|
3155
|
+
} else if (byEntityType && byEntityId && mutualEntityType) {
|
|
3156
|
+
yield wsRepo.createSubscription(
|
|
3157
|
+
`${SUB_MUTUAL_TYPE}${byEntityType}#${byEntityId}#${mutualEntityType}`,
|
|
3158
|
+
connectionId,
|
|
3159
|
+
{
|
|
3160
|
+
subscriptionType: "mutual-type",
|
|
3161
|
+
byEntityType,
|
|
3162
|
+
byEntityId,
|
|
3163
|
+
entityType: mutualEntityType,
|
|
3164
|
+
subscribedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3165
|
+
}
|
|
3166
|
+
);
|
|
3167
|
+
} else if (channel) {
|
|
3168
|
+
yield wsRepo.createSubscription(
|
|
3169
|
+
`${SUB_EPHEMERAL}${channel}`,
|
|
3170
|
+
connectionId,
|
|
3171
|
+
{
|
|
3172
|
+
subscriptionType: "ephemeral",
|
|
3173
|
+
channel,
|
|
3174
|
+
subscribedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3175
|
+
}
|
|
3176
|
+
);
|
|
3177
|
+
} else {
|
|
3178
|
+
return { statusCode: 400, body: "Invalid subscription parameters" };
|
|
3179
|
+
}
|
|
3180
|
+
const ackMessage = {
|
|
3181
|
+
type: "ack",
|
|
3182
|
+
id: message.id,
|
|
3183
|
+
payload: { action: "subscribe", success: true }
|
|
3184
|
+
};
|
|
3185
|
+
yield managementApi.send(
|
|
3186
|
+
new PostToConnectionCommand({
|
|
3187
|
+
ConnectionId: connectionId,
|
|
3188
|
+
Data: JSON.stringify(ackMessage)
|
|
3189
|
+
})
|
|
3190
|
+
);
|
|
3191
|
+
return { statusCode: 200, body: "Subscribed" };
|
|
3192
|
+
}
|
|
3193
|
+
case "unsubscribe": {
|
|
3194
|
+
const {
|
|
3195
|
+
entityType,
|
|
3196
|
+
byEntityType,
|
|
3197
|
+
byEntityId,
|
|
3198
|
+
mutualEntityType,
|
|
3199
|
+
channel
|
|
3200
|
+
} = message.payload;
|
|
3201
|
+
if (entityType && !byEntityType) {
|
|
3202
|
+
yield wsRepo.deleteSubscription(
|
|
3203
|
+
`${SUB_ENTITY_TYPE}${entityType}`,
|
|
3204
|
+
connectionId
|
|
3205
|
+
);
|
|
3206
|
+
} else if (byEntityType && byEntityId && mutualEntityType) {
|
|
3207
|
+
yield wsRepo.deleteSubscription(
|
|
3208
|
+
`${SUB_MUTUAL_TYPE}${byEntityType}#${byEntityId}#${mutualEntityType}`,
|
|
3209
|
+
connectionId
|
|
3210
|
+
);
|
|
3211
|
+
} else if (channel) {
|
|
3212
|
+
yield wsRepo.deleteSubscription(
|
|
3213
|
+
`${SUB_EPHEMERAL}${channel}`,
|
|
3214
|
+
connectionId
|
|
3215
|
+
);
|
|
3216
|
+
}
|
|
3217
|
+
const ackMessage = {
|
|
3218
|
+
type: "ack",
|
|
3219
|
+
id: message.id,
|
|
3220
|
+
payload: { action: "unsubscribe", success: true }
|
|
3221
|
+
};
|
|
3222
|
+
yield managementApi.send(
|
|
3223
|
+
new PostToConnectionCommand({
|
|
3224
|
+
ConnectionId: connectionId,
|
|
3225
|
+
Data: JSON.stringify(ackMessage)
|
|
3226
|
+
})
|
|
3227
|
+
);
|
|
3228
|
+
return { statusCode: 200, body: "Unsubscribed" };
|
|
3229
|
+
}
|
|
3230
|
+
case "ping": {
|
|
3231
|
+
const pongMessage = {
|
|
3232
|
+
type: "pong",
|
|
3233
|
+
id: message.id,
|
|
3234
|
+
payload: { timestamp: Date.now() }
|
|
3235
|
+
};
|
|
3236
|
+
yield managementApi.send(
|
|
3237
|
+
new PostToConnectionCommand({
|
|
3238
|
+
ConnectionId: connectionId,
|
|
3239
|
+
Data: JSON.stringify(pongMessage)
|
|
3240
|
+
})
|
|
3241
|
+
);
|
|
3242
|
+
return { statusCode: 200, body: "Pong" };
|
|
3243
|
+
}
|
|
3244
|
+
case "ephemeral": {
|
|
3245
|
+
const { channel, data } = message.payload;
|
|
3246
|
+
if (!channel) {
|
|
3247
|
+
return { statusCode: 400, body: "Missing channel" };
|
|
3248
|
+
}
|
|
3249
|
+
const conn = yield wsRepo.getConnection(connectionId);
|
|
3250
|
+
const senderId = conn == null ? void 0 : conn.entityId;
|
|
3251
|
+
const subKey = `${SUB_EPHEMERAL}${channel}`;
|
|
3252
|
+
const ephemeralMessage = {
|
|
3253
|
+
type: "ephemeral",
|
|
3254
|
+
id: ulid3(),
|
|
3255
|
+
payload: { channel, data, senderId }
|
|
3256
|
+
};
|
|
3257
|
+
yield broadcastToSubscribers(
|
|
3258
|
+
managementApi,
|
|
3259
|
+
wsRepo,
|
|
3260
|
+
subKey,
|
|
3261
|
+
ephemeralMessage,
|
|
3262
|
+
connectionId
|
|
3263
|
+
// Exclude sender
|
|
3264
|
+
);
|
|
3265
|
+
return { statusCode: 200, body: "Broadcasted" };
|
|
3266
|
+
}
|
|
3267
|
+
default:
|
|
3268
|
+
return { statusCode: 400, body: "Unknown action" };
|
|
3269
|
+
}
|
|
3270
|
+
} catch (error) {
|
|
3271
|
+
console.error("Error handling message:", error);
|
|
3272
|
+
try {
|
|
3273
|
+
const errorMessage = {
|
|
3274
|
+
type: "error",
|
|
3275
|
+
id: message.id,
|
|
3276
|
+
payload: { message: "Internal server error" }
|
|
3277
|
+
};
|
|
3278
|
+
yield managementApi.send(
|
|
3279
|
+
new PostToConnectionCommand({
|
|
3280
|
+
ConnectionId: connectionId,
|
|
3281
|
+
Data: JSON.stringify(errorMessage)
|
|
3282
|
+
})
|
|
3283
|
+
);
|
|
3284
|
+
} catch (e) {
|
|
3285
|
+
}
|
|
3286
|
+
return { statusCode: 500, body: "Internal server error" };
|
|
3287
|
+
}
|
|
3288
|
+
});
|
|
3289
|
+
var broadcast = (container) => (event) => __async(null, null, function* () {
|
|
3290
|
+
var _a, _b, _c, _d;
|
|
3291
|
+
const wsRepo = container.websocketRepository;
|
|
3292
|
+
const wsEndpoint = getWsEndpoint();
|
|
3293
|
+
const managementApi = new ApiGatewayManagementApiClient({
|
|
3294
|
+
endpoint: wsEndpoint
|
|
3295
|
+
});
|
|
3296
|
+
for (const record of event.Records) {
|
|
3297
|
+
const isInsert = record.eventName === "INSERT";
|
|
3298
|
+
const isModify = record.eventName === "MODIFY";
|
|
3299
|
+
const isRemove = record.eventName === "REMOVE";
|
|
3300
|
+
if (!isInsert && !isModify && !isRemove) continue;
|
|
3301
|
+
const newImage = (_a = record.dynamodb) == null ? void 0 : _a.NewImage;
|
|
3302
|
+
const oldImage = (_b = record.dynamodb) == null ? void 0 : _b.OldImage;
|
|
3303
|
+
const image = newImage || oldImage;
|
|
3304
|
+
if (!image) continue;
|
|
3305
|
+
const pk = ((_c = image.PK) == null ? void 0 : _c.S) || "";
|
|
3306
|
+
const sk = ((_d = image.SK) == null ? void 0 : _d.S) || "";
|
|
3307
|
+
const pkParts = pk.split("#");
|
|
3308
|
+
if (pkParts.length < 2) continue;
|
|
3309
|
+
const firstPart = pkParts[0];
|
|
3310
|
+
if (firstPart === firstPart.toUpperCase() || firstPart.includes(":")) {
|
|
3311
|
+
continue;
|
|
3312
|
+
}
|
|
3313
|
+
const entityType = pkParts[0];
|
|
3314
|
+
const entityId = pkParts[1];
|
|
3315
|
+
const isMutual = !sk.startsWith("#METADATA#") && sk.includes("#");
|
|
3316
|
+
try {
|
|
3317
|
+
if (isMutual) {
|
|
3318
|
+
const skParts = sk.split("#");
|
|
3319
|
+
const mutualEntityType = skParts[0];
|
|
3320
|
+
const byEntityId = entityId;
|
|
3321
|
+
const subKey = `${SUB_MUTUAL_TYPE}${entityType}#${byEntityId}#${mutualEntityType}`;
|
|
3322
|
+
const subscribers = yield wsRepo.querySubscriptionsByKey(subKey);
|
|
3323
|
+
if (subscribers.length) {
|
|
3324
|
+
let eventType;
|
|
3325
|
+
if (isInsert) eventType = "mutual.created";
|
|
3326
|
+
else if (isModify) eventType = "mutual.updated";
|
|
3327
|
+
else eventType = "mutual.deleted";
|
|
3328
|
+
const message = {
|
|
3329
|
+
type: eventType,
|
|
3330
|
+
id: ulid3(),
|
|
3331
|
+
payload: {
|
|
3332
|
+
byEntityType: entityType,
|
|
3333
|
+
byEntityId,
|
|
3334
|
+
mutualEntityType,
|
|
3335
|
+
entityId: skParts[1],
|
|
3336
|
+
data: isRemove ? void 0 : unmarshall4(image)
|
|
3337
|
+
}
|
|
3338
|
+
};
|
|
3339
|
+
yield broadcastToSubscribers(
|
|
3340
|
+
managementApi,
|
|
3341
|
+
wsRepo,
|
|
3342
|
+
subKey,
|
|
3343
|
+
message
|
|
3344
|
+
);
|
|
3345
|
+
}
|
|
3346
|
+
} else {
|
|
3347
|
+
const subKey = `${SUB_ENTITY_TYPE}${entityType}`;
|
|
3348
|
+
const subscribers = yield wsRepo.querySubscriptionsByKey(subKey);
|
|
3349
|
+
if (subscribers.length) {
|
|
3350
|
+
let eventType;
|
|
3351
|
+
if (isInsert) eventType = "entity.created";
|
|
3352
|
+
else if (isModify) eventType = "entity.updated";
|
|
3353
|
+
else eventType = "entity.deleted";
|
|
3354
|
+
const message = {
|
|
3355
|
+
type: eventType,
|
|
3356
|
+
id: ulid3(),
|
|
3357
|
+
payload: {
|
|
3358
|
+
entityType,
|
|
3359
|
+
entityId,
|
|
3360
|
+
data: isRemove ? void 0 : unmarshall4(image)
|
|
3361
|
+
}
|
|
3362
|
+
};
|
|
3363
|
+
yield broadcastToSubscribers(
|
|
3364
|
+
managementApi,
|
|
3365
|
+
wsRepo,
|
|
3366
|
+
subKey,
|
|
3367
|
+
message
|
|
3368
|
+
);
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
yield broadcastToFeedSubscribers(
|
|
3372
|
+
managementApi,
|
|
3373
|
+
wsRepo,
|
|
3374
|
+
entityType,
|
|
3375
|
+
entityId,
|
|
3376
|
+
isMutual ? sk.split("#")[0] : entityType,
|
|
3377
|
+
// the changed entity type
|
|
3378
|
+
isMutual ? {
|
|
3379
|
+
type: isInsert ? "mutual.created" : isModify ? "mutual.updated" : "mutual.deleted",
|
|
3380
|
+
id: ulid3(),
|
|
3381
|
+
payload: {
|
|
3382
|
+
byEntityType: entityType,
|
|
3383
|
+
byEntityId: entityId,
|
|
3384
|
+
mutualEntityType: sk.split("#")[0],
|
|
3385
|
+
entityId: sk.split("#")[1],
|
|
3386
|
+
data: isRemove ? void 0 : unmarshall4(image)
|
|
3387
|
+
}
|
|
3388
|
+
} : {
|
|
3389
|
+
type: isInsert ? "entity.created" : isModify ? "entity.updated" : "entity.deleted",
|
|
3390
|
+
id: ulid3(),
|
|
3391
|
+
payload: {
|
|
3392
|
+
entityType,
|
|
3393
|
+
entityId,
|
|
3394
|
+
data: isRemove ? void 0 : unmarshall4(image)
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
);
|
|
3398
|
+
} catch (error) {
|
|
3399
|
+
console.error("Error broadcasting:", error);
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
});
|
|
3403
|
+
function broadcastToSubscribers(managementApi, wsRepo, subKey, message, excludeConnectionId) {
|
|
3404
|
+
return __async(this, null, function* () {
|
|
3405
|
+
const subscribers = yield wsRepo.querySubscriptionsByKey(subKey);
|
|
3406
|
+
if (!subscribers.length) return;
|
|
3407
|
+
const messageData = JSON.stringify(message);
|
|
3408
|
+
const sends = subscribers.filter((subscriber) => {
|
|
3409
|
+
const id = subscriber.connectionId;
|
|
3410
|
+
return !excludeConnectionId || id !== excludeConnectionId;
|
|
3411
|
+
}).map((subscriber) => __async(null, null, function* () {
|
|
3412
|
+
var _a;
|
|
3413
|
+
try {
|
|
3414
|
+
yield managementApi.send(
|
|
3415
|
+
new PostToConnectionCommand({
|
|
3416
|
+
ConnectionId: subscriber.connectionId,
|
|
3417
|
+
Data: messageData
|
|
3418
|
+
})
|
|
3419
|
+
);
|
|
3420
|
+
} catch (error) {
|
|
3421
|
+
const isGone = (error == null ? void 0 : error.name) === "GoneException" || ((_a = error == null ? void 0 : error.$metadata) == null ? void 0 : _a.httpStatusCode) === 410;
|
|
3422
|
+
if (isGone) {
|
|
3423
|
+
yield wsRepo.deleteSubscription(subKey, subscriber.connectionId).catch(
|
|
3424
|
+
(e) => console.warn("Failed to clean up stale subscription:", e)
|
|
3425
|
+
);
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
}));
|
|
3429
|
+
yield Promise.allSettled(sends);
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
function broadcastToFeedSubscribers(managementApi, wsRepo, byEntityType, byEntityId, changedEntityType, message) {
|
|
3433
|
+
return __async(this, null, function* () {
|
|
3434
|
+
var _a;
|
|
3435
|
+
const connections = yield wsRepo.queryMutualConnections(
|
|
3436
|
+
byEntityType,
|
|
3437
|
+
byEntityId
|
|
3438
|
+
);
|
|
3439
|
+
if (!connections.length) return;
|
|
3440
|
+
const connectedEntities = new Set(
|
|
3441
|
+
connections.map((c) => `${c.entityType}:${c.entityId}`)
|
|
3442
|
+
);
|
|
3443
|
+
connectedEntities.add(`${byEntityType}:${byEntityId}`);
|
|
3444
|
+
const sentConnections = /* @__PURE__ */ new Set();
|
|
3445
|
+
for (const connEntity of connectedEntities) {
|
|
3446
|
+
const [entityType, entityId] = connEntity.split(":");
|
|
3447
|
+
const feedSubs = yield wsRepo.queryFeedSubscriptions(entityType, entityId);
|
|
3448
|
+
if (!feedSubs.length) continue;
|
|
3449
|
+
for (const feedSub of feedSubs) {
|
|
3450
|
+
const feedTypes = feedSub.feedTypes;
|
|
3451
|
+
const connectionId = feedSub.connectionId;
|
|
3452
|
+
if (feedTypes && !feedTypes.includes(changedEntityType)) continue;
|
|
3453
|
+
if (sentConnections.has(connectionId)) continue;
|
|
3454
|
+
sentConnections.add(connectionId);
|
|
3455
|
+
try {
|
|
3456
|
+
yield managementApi.send(
|
|
3457
|
+
new PostToConnectionCommand({
|
|
3458
|
+
ConnectionId: connectionId,
|
|
3459
|
+
Data: JSON.stringify(message)
|
|
3460
|
+
})
|
|
3461
|
+
);
|
|
3462
|
+
} catch (error) {
|
|
3463
|
+
const isGone = (error == null ? void 0 : error.name) === "GoneException" || ((_a = error == null ? void 0 : error.$metadata) == null ? void 0 : _a.httpStatusCode) === 410;
|
|
3464
|
+
if (isGone) {
|
|
3465
|
+
yield wsRepo.deleteSubscription(
|
|
3466
|
+
`SUB#FEED#${entityType}#${entityId}`,
|
|
3467
|
+
connectionId
|
|
3468
|
+
).catch(
|
|
3469
|
+
(e) => console.warn("Failed to clean up stale feed subscription:", e)
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3477
|
+
|
|
2858
3478
|
// services/DependencyContainer.ts
|
|
2859
3479
|
import { DynamoDB } from "@aws-sdk/client-dynamodb";
|
|
2860
3480
|
|
|
@@ -7091,7 +7711,16 @@ var UpdateEntityController = class {
|
|
|
7091
7711
|
const accountId = c.req.header("account-id");
|
|
7092
7712
|
const { entityType, entityId } = c.req.param();
|
|
7093
7713
|
const body = yield c.req.json();
|
|
7094
|
-
const _a = body, { $where: where } = _a, entityPayload = __objRest(_a, ["$where"]);
|
|
7714
|
+
const _a = body, { $condition: condition, $where: where } = _a, entityPayload = __objRest(_a, ["$condition", "$where"]);
|
|
7715
|
+
if (condition !== void 0) {
|
|
7716
|
+
if (typeof condition !== "string" || condition.trim().length === 0) {
|
|
7717
|
+
c.status(httpStatus8.BAD_REQUEST);
|
|
7718
|
+
return c.json({
|
|
7719
|
+
code: "API_VALIDATION_ERROR",
|
|
7720
|
+
message: "$condition must be a non-empty string"
|
|
7721
|
+
});
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7095
7724
|
const errorContext = {
|
|
7096
7725
|
accountId,
|
|
7097
7726
|
"req.params": c.req.param(),
|
|
@@ -7103,6 +7732,7 @@ var UpdateEntityController = class {
|
|
|
7103
7732
|
entityId,
|
|
7104
7733
|
entityPayload,
|
|
7105
7734
|
accountId,
|
|
7735
|
+
condition,
|
|
7106
7736
|
where
|
|
7107
7737
|
});
|
|
7108
7738
|
errorContext.entity = entity;
|
|
@@ -7121,11 +7751,15 @@ var UpdateEntityController = class {
|
|
|
7121
7751
|
c.status(httpStatus8.NOT_FOUND);
|
|
7122
7752
|
return c.json(__spreadValues({}, err.toJSON()));
|
|
7123
7753
|
}
|
|
7124
|
-
if (err instanceof StandardError && err.code === StandardErrorCode.
|
|
7754
|
+
if (err instanceof StandardError && err.code === StandardErrorCode.INVALID_CONDITION) {
|
|
7125
7755
|
c.status(httpStatus8.BAD_REQUEST);
|
|
7126
7756
|
return c.json(__spreadValues({}, err.toJSON()));
|
|
7127
7757
|
}
|
|
7128
|
-
if (err instanceof StandardError && err.code === StandardErrorCode.
|
|
7758
|
+
if (err instanceof StandardError && err.code === StandardErrorCode.UNIQUE_VALUE_EXISTS) {
|
|
7759
|
+
c.status(httpStatus8.BAD_REQUEST);
|
|
7760
|
+
return c.json(__spreadValues({}, err.toJSON()));
|
|
7761
|
+
}
|
|
7762
|
+
if (err instanceof StandardError && err.code === StandardErrorCode.CONDITIONAL_CHECK_FAILED) {
|
|
7129
7763
|
c.status(httpStatus8.CONFLICT);
|
|
7130
7764
|
return c.json(__spreadValues({}, err.toJSON()));
|
|
7131
7765
|
}
|
|
@@ -7147,11 +7781,21 @@ var AdjustEntityController = class {
|
|
|
7147
7781
|
constructor(entityService) {
|
|
7148
7782
|
this.entityService = entityService;
|
|
7149
7783
|
this.controller = createMiddleware9((c) => __async(this, null, function* () {
|
|
7150
|
-
var
|
|
7784
|
+
var _b;
|
|
7151
7785
|
const accountId = c.req.header("account-id") || "";
|
|
7152
7786
|
const { entityType, entityId } = c.req.param();
|
|
7153
7787
|
const body = yield c.req.json();
|
|
7154
|
-
|
|
7788
|
+
const _a = body, { $condition: condition } = _a, adjustments = __objRest(_a, ["$condition"]);
|
|
7789
|
+
if (condition !== void 0) {
|
|
7790
|
+
if (typeof condition !== "string" || condition.trim().length === 0) {
|
|
7791
|
+
c.status(httpStatus9.BAD_REQUEST);
|
|
7792
|
+
return c.json({
|
|
7793
|
+
code: "API_VALIDATION_ERROR",
|
|
7794
|
+
message: "$condition must be a non-empty string"
|
|
7795
|
+
});
|
|
7796
|
+
}
|
|
7797
|
+
}
|
|
7798
|
+
for (const [key, value] of Object.entries(adjustments)) {
|
|
7155
7799
|
if (typeof value !== "number") {
|
|
7156
7800
|
c.status(httpStatus9.BAD_REQUEST);
|
|
7157
7801
|
return c.json({
|
|
@@ -7164,8 +7808,9 @@ var AdjustEntityController = class {
|
|
|
7164
7808
|
const entity = yield this.entityService.adjustEntity({
|
|
7165
7809
|
entityType,
|
|
7166
7810
|
entityId,
|
|
7167
|
-
adjustments
|
|
7168
|
-
accountId
|
|
7811
|
+
adjustments,
|
|
7812
|
+
accountId,
|
|
7813
|
+
condition
|
|
7169
7814
|
});
|
|
7170
7815
|
c.status(httpStatus9.OK);
|
|
7171
7816
|
return c.json(entity.toJSON());
|
|
@@ -7174,7 +7819,11 @@ var AdjustEntityController = class {
|
|
|
7174
7819
|
c.status(httpStatus9.NOT_FOUND);
|
|
7175
7820
|
return c.json(__spreadValues({}, err.toJSON()));
|
|
7176
7821
|
}
|
|
7177
|
-
if (
|
|
7822
|
+
if (err instanceof StandardError && err.code === StandardErrorCode.INVALID_CONDITION) {
|
|
7823
|
+
c.status(httpStatus9.BAD_REQUEST);
|
|
7824
|
+
return c.json(__spreadValues({}, err.toJSON()));
|
|
7825
|
+
}
|
|
7826
|
+
if ((err == null ? void 0 : err.name) === "ConditionalCheckFailedException" || ((_b = err == null ? void 0 : err.__type) == null ? void 0 : _b.includes("ConditionalCheckFailed"))) {
|
|
7178
7827
|
c.status(httpStatus9.CONFLICT);
|
|
7179
7828
|
return c.json({
|
|
7180
7829
|
code: "ADJUSTMENT_CONSTRAINT_VIOLATED",
|
|
@@ -7479,6 +8128,9 @@ var UpdateMutualController = class {
|
|
|
7479
8128
|
}
|
|
7480
8129
|
};
|
|
7481
8130
|
|
|
8131
|
+
// services/entity.service.ts
|
|
8132
|
+
import { marshall as marshall6 } from "@aws-sdk/util-dynamodb";
|
|
8133
|
+
|
|
7482
8134
|
// data/utils/build-condition-expression.ts
|
|
7483
8135
|
import { marshall as marshall5 } from "@aws-sdk/util-dynamodb";
|
|
7484
8136
|
function buildConditionExpression(where) {
|
|
@@ -7540,6 +8192,55 @@ function buildConditionExpression(where) {
|
|
|
7540
8192
|
};
|
|
7541
8193
|
}
|
|
7542
8194
|
|
|
8195
|
+
// services/resolve-condition.ts
|
|
8196
|
+
function resolveAdjustmentCondition(_0) {
|
|
8197
|
+
return __async(this, arguments, function* ({
|
|
8198
|
+
conditionName,
|
|
8199
|
+
conditions,
|
|
8200
|
+
adjustments,
|
|
8201
|
+
getEntityData
|
|
8202
|
+
}) {
|
|
8203
|
+
if (!Object.hasOwn(conditions, conditionName)) {
|
|
8204
|
+
throw new StandardError(
|
|
8205
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
8206
|
+
`Unknown adjustment condition: '${conditionName}'`
|
|
8207
|
+
);
|
|
8208
|
+
}
|
|
8209
|
+
const condition = conditions[conditionName];
|
|
8210
|
+
let resolved;
|
|
8211
|
+
if (typeof condition === "function") {
|
|
8212
|
+
const data = yield getEntityData();
|
|
8213
|
+
resolved = condition(data, adjustments);
|
|
8214
|
+
} else {
|
|
8215
|
+
resolved = condition;
|
|
8216
|
+
}
|
|
8217
|
+
return buildConditionExpression(resolved);
|
|
8218
|
+
});
|
|
8219
|
+
}
|
|
8220
|
+
function resolveUpdateCondition(_0) {
|
|
8221
|
+
return __async(this, arguments, function* ({
|
|
8222
|
+
conditionName,
|
|
8223
|
+
conditions,
|
|
8224
|
+
getEntityData
|
|
8225
|
+
}) {
|
|
8226
|
+
if (!Object.hasOwn(conditions, conditionName)) {
|
|
8227
|
+
throw new StandardError(
|
|
8228
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
8229
|
+
`Unknown update condition: '${conditionName}'`
|
|
8230
|
+
);
|
|
8231
|
+
}
|
|
8232
|
+
const condition = conditions[conditionName];
|
|
8233
|
+
let resolved;
|
|
8234
|
+
if (typeof condition === "function") {
|
|
8235
|
+
const data = yield getEntityData();
|
|
8236
|
+
resolved = condition(data);
|
|
8237
|
+
} else {
|
|
8238
|
+
resolved = condition;
|
|
8239
|
+
}
|
|
8240
|
+
return buildConditionExpression(resolved);
|
|
8241
|
+
});
|
|
8242
|
+
}
|
|
8243
|
+
|
|
7543
8244
|
// services/entity.service.ts
|
|
7544
8245
|
var EntityService = class {
|
|
7545
8246
|
constructor(EntityConfig, EmailAuthEnabledEntities, entityRepository, publishEvent2, entityServiceLifeCycle) {
|
|
@@ -7595,34 +8296,59 @@ var EntityService = class {
|
|
|
7595
8296
|
entityType,
|
|
7596
8297
|
entityId,
|
|
7597
8298
|
adjustments,
|
|
7598
|
-
accountId
|
|
8299
|
+
accountId,
|
|
8300
|
+
condition
|
|
7599
8301
|
}) {
|
|
7600
|
-
var _a, _b, _c
|
|
7601
|
-
const
|
|
7602
|
-
|
|
7603
|
-
|
|
8302
|
+
var _a, _b, _c;
|
|
8303
|
+
const entityConfig = this.EntityConfig[entityType];
|
|
8304
|
+
const adjustmentConditions = entityConfig == null ? void 0 : entityConfig.adjustmentConditions;
|
|
8305
|
+
const rawConstraints = entityConfig == null ? void 0 : entityConfig.adjustmentConstraints;
|
|
8306
|
+
let opts;
|
|
8307
|
+
if (adjustmentConditions) {
|
|
8308
|
+
if (!condition) {
|
|
8309
|
+
throw new StandardError(
|
|
8310
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
8311
|
+
"Entity has adjustmentConditions defined; $condition is required for adjustEntity"
|
|
8312
|
+
);
|
|
8313
|
+
}
|
|
8314
|
+
opts = yield resolveAdjustmentCondition({
|
|
8315
|
+
conditionName: condition,
|
|
8316
|
+
conditions: adjustmentConditions,
|
|
8317
|
+
adjustments,
|
|
8318
|
+
getEntityData: () => __async(this, null, function* () {
|
|
8319
|
+
var _a2;
|
|
8320
|
+
const entity2 = yield this.entityRepository.getEntity(entityType, entityId);
|
|
8321
|
+
return (_a2 = entity2 == null ? void 0 : entity2.data) != null ? _a2 : {};
|
|
8322
|
+
})
|
|
8323
|
+
});
|
|
8324
|
+
} else if (rawConstraints) {
|
|
8325
|
+
console.warn(
|
|
8326
|
+
"[monorise] adjustmentConstraints is deprecated. Use adjustmentConditions instead."
|
|
8327
|
+
);
|
|
8328
|
+
let resolvedConstraints = rawConstraints;
|
|
7604
8329
|
const hasDynamicFields = Object.values(rawConstraints).some(
|
|
7605
8330
|
(c) => c.minField || c.maxField
|
|
7606
8331
|
);
|
|
7607
8332
|
if (hasDynamicFields) {
|
|
7608
8333
|
const currentEntity = yield this.entityRepository.getEntity(entityType, entityId);
|
|
7609
|
-
const data = (
|
|
8334
|
+
const data = (_a = currentEntity == null ? void 0 : currentEntity.data) != null ? _a : {};
|
|
7610
8335
|
resolvedConstraints = {};
|
|
7611
8336
|
for (const [field, constraint] of Object.entries(rawConstraints)) {
|
|
7612
8337
|
const resolved = {};
|
|
7613
8338
|
if (constraint.min !== void 0) resolved.min = constraint.min;
|
|
7614
8339
|
if (constraint.max !== void 0) resolved.max = constraint.max;
|
|
7615
|
-
if (constraint.minField) resolved.min = (
|
|
7616
|
-
if (constraint.maxField) resolved.max = (
|
|
8340
|
+
if (constraint.minField) resolved.min = (_b = data[constraint.minField]) != null ? _b : 0;
|
|
8341
|
+
if (constraint.maxField) resolved.max = (_c = data[constraint.maxField]) != null ? _c : Number.MAX_SAFE_INTEGER;
|
|
7617
8342
|
resolvedConstraints[field] = resolved;
|
|
7618
8343
|
}
|
|
7619
8344
|
}
|
|
8345
|
+
opts = this.buildLegacyAdjustCondition(adjustments, resolvedConstraints);
|
|
7620
8346
|
}
|
|
7621
8347
|
const entity = yield this.entityRepository.adjustEntity(
|
|
7622
8348
|
entityType,
|
|
7623
8349
|
entityId,
|
|
7624
8350
|
adjustments,
|
|
7625
|
-
|
|
8351
|
+
opts
|
|
7626
8352
|
);
|
|
7627
8353
|
yield this.publishEvent({
|
|
7628
8354
|
event: EVENT.CORE.ENTITY_UPDATED,
|
|
@@ -7641,9 +8367,10 @@ var EntityService = class {
|
|
|
7641
8367
|
entityId,
|
|
7642
8368
|
entityPayload,
|
|
7643
8369
|
accountId,
|
|
8370
|
+
condition,
|
|
7644
8371
|
where
|
|
7645
8372
|
}) {
|
|
7646
|
-
var _a, _b;
|
|
8373
|
+
var _a, _b, _c;
|
|
7647
8374
|
const errorContext = {};
|
|
7648
8375
|
try {
|
|
7649
8376
|
const entitySchema = this.EntityConfig[entityType].baseSchema;
|
|
@@ -7657,7 +8384,30 @@ var EntityService = class {
|
|
|
7657
8384
|
const parsedEntityPayload = entitySchema.parse(entityPayload);
|
|
7658
8385
|
const parsedMutualPayload = mutualSchema == null ? void 0 : mutualSchema.parse(entityPayload);
|
|
7659
8386
|
errorContext.parsedMutualPayload = parsedMutualPayload;
|
|
7660
|
-
|
|
8387
|
+
let opts;
|
|
8388
|
+
if (condition) {
|
|
8389
|
+
const updateConditions = (_b = this.EntityConfig[entityType]) == null ? void 0 : _b.updateConditions;
|
|
8390
|
+
if (!updateConditions) {
|
|
8391
|
+
throw new StandardError(
|
|
8392
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
8393
|
+
`Entity '${entityType}' has no updateConditions defined`
|
|
8394
|
+
);
|
|
8395
|
+
}
|
|
8396
|
+
opts = yield resolveUpdateCondition({
|
|
8397
|
+
conditionName: condition,
|
|
8398
|
+
conditions: updateConditions,
|
|
8399
|
+
getEntityData: () => __async(this, null, function* () {
|
|
8400
|
+
var _a2;
|
|
8401
|
+
const entity2 = yield this.entityRepository.getEntity(entityType, entityId);
|
|
8402
|
+
return (_a2 = entity2 == null ? void 0 : entity2.data) != null ? _a2 : {};
|
|
8403
|
+
})
|
|
8404
|
+
});
|
|
8405
|
+
} else if (where && Object.keys(where).length > 0) {
|
|
8406
|
+
console.warn(
|
|
8407
|
+
"[monorise] $where is deprecated. Use named conditions via $condition instead."
|
|
8408
|
+
);
|
|
8409
|
+
opts = buildConditionExpression(where);
|
|
8410
|
+
}
|
|
7661
8411
|
const entity = yield this.entityRepository.updateEntity(
|
|
7662
8412
|
entityType,
|
|
7663
8413
|
entityId,
|
|
@@ -7670,7 +8420,7 @@ var EntityService = class {
|
|
|
7670
8420
|
const byEntityId = entityId;
|
|
7671
8421
|
const publishEventPromises = [];
|
|
7672
8422
|
for (const [fieldKey, config] of Object.entries(
|
|
7673
|
-
((
|
|
8423
|
+
((_c = this.EntityConfig[entityType].mutual) == null ? void 0 : _c.mutualFields) || {}
|
|
7674
8424
|
)) {
|
|
7675
8425
|
const toMutualIds = config.toMutualIds;
|
|
7676
8426
|
const mutualPayload = parsedMutualPayload[fieldKey];
|
|
@@ -7726,10 +8476,39 @@ var EntityService = class {
|
|
|
7726
8476
|
});
|
|
7727
8477
|
});
|
|
7728
8478
|
}
|
|
8479
|
+
/** @deprecated Converts legacy adjustmentConstraints to condition expression opts. */
|
|
8480
|
+
buildLegacyAdjustCondition(adjustments, constraints) {
|
|
8481
|
+
const conditionParts = [];
|
|
8482
|
+
const names = { "#data": "data" };
|
|
8483
|
+
const values = {};
|
|
8484
|
+
for (const [field, constraint] of Object.entries(constraints)) {
|
|
8485
|
+
const delta = adjustments[field];
|
|
8486
|
+
if (delta === void 0) continue;
|
|
8487
|
+
const namePlaceholder = `#where_${field}`;
|
|
8488
|
+
names[namePlaceholder] = field;
|
|
8489
|
+
const fieldRef = `#data.${namePlaceholder}`;
|
|
8490
|
+
if (constraint.min !== void 0 && delta < 0) {
|
|
8491
|
+
const valKey = `:where_${field}_min_threshold`;
|
|
8492
|
+
conditionParts.push(`${fieldRef} >= ${valKey}`);
|
|
8493
|
+
values[valKey] = constraint.min - delta;
|
|
8494
|
+
}
|
|
8495
|
+
if (constraint.max !== void 0 && delta > 0) {
|
|
8496
|
+
const valKey = `:where_${field}_max_threshold`;
|
|
8497
|
+
conditionParts.push(`${fieldRef} <= ${valKey}`);
|
|
8498
|
+
values[valKey] = constraint.max - delta;
|
|
8499
|
+
}
|
|
8500
|
+
}
|
|
8501
|
+
if (conditionParts.length === 0) return void 0;
|
|
8502
|
+
return {
|
|
8503
|
+
ConditionExpression: conditionParts.join(" AND "),
|
|
8504
|
+
ExpressionAttributeNames: names,
|
|
8505
|
+
ExpressionAttributeValues: marshall6(values)
|
|
8506
|
+
};
|
|
8507
|
+
}
|
|
7729
8508
|
};
|
|
7730
8509
|
|
|
7731
8510
|
// services/mutual.service.ts
|
|
7732
|
-
import { ulid as
|
|
8511
|
+
import { ulid as ulid4 } from "ulid";
|
|
7733
8512
|
var MutualService = class {
|
|
7734
8513
|
constructor(entityRepository, mutualRepository, publishEvent2, ddbUtils, entityServiceLifeCycle) {
|
|
7735
8514
|
this.entityRepository = entityRepository;
|
|
@@ -7789,7 +8568,7 @@ var MutualService = class {
|
|
|
7789
8568
|
entityId,
|
|
7790
8569
|
entityData,
|
|
7791
8570
|
parsedMutualPayload,
|
|
7792
|
-
mutualId ||
|
|
8571
|
+
mutualId || ulid4(),
|
|
7793
8572
|
currentDatetime,
|
|
7794
8573
|
currentDatetime,
|
|
7795
8574
|
currentDatetime
|
|
@@ -7967,6 +8746,116 @@ var ListTagsController = class {
|
|
|
7967
8746
|
}
|
|
7968
8747
|
};
|
|
7969
8748
|
|
|
8749
|
+
// controllers/transaction/execute-transaction.controller.ts
|
|
8750
|
+
import { createMiddleware as createMiddleware17 } from "hono/factory";
|
|
8751
|
+
import httpStatus15 from "http-status";
|
|
8752
|
+
var ExecuteTransactionController = class {
|
|
8753
|
+
constructor(transactionService) {
|
|
8754
|
+
this.transactionService = transactionService;
|
|
8755
|
+
// biome-ignore lint/suspicious/noExplicitAny: Hono createMiddleware requires consistent return types
|
|
8756
|
+
this.controller = createMiddleware17((c) => __async(this, null, function* () {
|
|
8757
|
+
var _a;
|
|
8758
|
+
const accountId = c.req.header("account-id") || "";
|
|
8759
|
+
const body = yield c.req.json();
|
|
8760
|
+
if (!body.operations || !Array.isArray(body.operations)) {
|
|
8761
|
+
c.status(httpStatus15.BAD_REQUEST);
|
|
8762
|
+
return c.json({
|
|
8763
|
+
code: "API_VALIDATION_ERROR",
|
|
8764
|
+
message: 'Request body must contain an "operations" array'
|
|
8765
|
+
});
|
|
8766
|
+
}
|
|
8767
|
+
try {
|
|
8768
|
+
const result = yield this.transactionService.executeTransaction(
|
|
8769
|
+
body.operations,
|
|
8770
|
+
accountId
|
|
8771
|
+
);
|
|
8772
|
+
c.status(httpStatus15.OK);
|
|
8773
|
+
return c.json(result);
|
|
8774
|
+
} catch (err) {
|
|
8775
|
+
if (((_a = err.constructor) == null ? void 0 : _a.name) === "ZodError") {
|
|
8776
|
+
c.status(httpStatus15.BAD_REQUEST);
|
|
8777
|
+
return c.json({
|
|
8778
|
+
code: "API_VALIDATION_ERROR",
|
|
8779
|
+
message: "Validation failed",
|
|
8780
|
+
details: err.flatten()
|
|
8781
|
+
});
|
|
8782
|
+
}
|
|
8783
|
+
if (err instanceof StandardError) {
|
|
8784
|
+
const code = err.code;
|
|
8785
|
+
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) {
|
|
8786
|
+
c.status(httpStatus15.BAD_REQUEST);
|
|
8787
|
+
return c.json(__spreadValues({}, err.toJSON()));
|
|
8788
|
+
}
|
|
8789
|
+
if (code === StandardErrorCode.TRANSACTION_FAILED || code === StandardErrorCode.CONDITIONAL_CHECK_FAILED || code === StandardErrorCode.UNIQUE_VALUE_EXISTS) {
|
|
8790
|
+
c.status(httpStatus15.CONFLICT);
|
|
8791
|
+
return c.json(__spreadValues({}, err.toJSON()));
|
|
8792
|
+
}
|
|
8793
|
+
}
|
|
8794
|
+
throw err;
|
|
8795
|
+
}
|
|
8796
|
+
}));
|
|
8797
|
+
}
|
|
8798
|
+
};
|
|
8799
|
+
|
|
8800
|
+
// controllers/ws/create-ticket.controller.ts
|
|
8801
|
+
import { createMiddleware as createMiddleware18 } from "hono/factory";
|
|
8802
|
+
import { ulid as ulid5 } from "ulid";
|
|
8803
|
+
var TICKET_TTL_SECONDS = 30 * 60;
|
|
8804
|
+
var CreateTicketController = class {
|
|
8805
|
+
constructor(container) {
|
|
8806
|
+
this.container = container;
|
|
8807
|
+
this.controller = createMiddleware18((c) => __async(this, null, function* () {
|
|
8808
|
+
var _a;
|
|
8809
|
+
const { entityType, entityId } = c.req.param();
|
|
8810
|
+
let feedTypes;
|
|
8811
|
+
try {
|
|
8812
|
+
const body = yield c.req.json();
|
|
8813
|
+
feedTypes = body.feedTypes;
|
|
8814
|
+
} catch (e) {
|
|
8815
|
+
}
|
|
8816
|
+
if (!feedTypes || feedTypes.length === 0) {
|
|
8817
|
+
const allConfigs = this.container.config.EntityConfig;
|
|
8818
|
+
const visited = /* @__PURE__ */ new Set();
|
|
8819
|
+
const queue = [entityType];
|
|
8820
|
+
while (queue.length > 0) {
|
|
8821
|
+
const current = queue.shift();
|
|
8822
|
+
if (!current) continue;
|
|
8823
|
+
if (visited.has(current)) continue;
|
|
8824
|
+
visited.add(current);
|
|
8825
|
+
const config = allConfigs[current];
|
|
8826
|
+
if ((_a = config == null ? void 0 : config.mutual) == null ? void 0 : _a.mutualFields) {
|
|
8827
|
+
for (const field of Object.values(
|
|
8828
|
+
config.mutual.mutualFields
|
|
8829
|
+
)) {
|
|
8830
|
+
if (!visited.has(field.entityType)) {
|
|
8831
|
+
queue.push(field.entityType);
|
|
8832
|
+
}
|
|
8833
|
+
}
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
visited.delete(entityType);
|
|
8837
|
+
feedTypes = Array.from(visited);
|
|
8838
|
+
}
|
|
8839
|
+
const ticket = ulid5();
|
|
8840
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
8841
|
+
const expiresAt = now + TICKET_TTL_SECONDS;
|
|
8842
|
+
yield this.container.websocketRepository.createTicket(
|
|
8843
|
+
ticket,
|
|
8844
|
+
entityType,
|
|
8845
|
+
entityId,
|
|
8846
|
+
feedTypes,
|
|
8847
|
+
expiresAt
|
|
8848
|
+
);
|
|
8849
|
+
const wsEndpoint = process.env.WEBSOCKET_URL || "";
|
|
8850
|
+
return c.json({
|
|
8851
|
+
ticket,
|
|
8852
|
+
wsUrl: wsEndpoint,
|
|
8853
|
+
expiresIn: TICKET_TTL_SECONDS
|
|
8854
|
+
});
|
|
8855
|
+
}));
|
|
8856
|
+
}
|
|
8857
|
+
};
|
|
8858
|
+
|
|
7970
8859
|
// services/entity-service-lifecycle.ts
|
|
7971
8860
|
var EntityServiceLifeCycle = class {
|
|
7972
8861
|
constructor(EntityConfig, publishEvent2, eventUtils) {
|
|
@@ -7999,6 +8888,435 @@ var EntityServiceLifeCycle = class {
|
|
|
7999
8888
|
}
|
|
8000
8889
|
};
|
|
8001
8890
|
|
|
8891
|
+
// services/transaction.service.ts
|
|
8892
|
+
import { TransactionCanceledException as TransactionCanceledException5 } from "@aws-sdk/client-dynamodb";
|
|
8893
|
+
import { ulid as ulid6 } from "ulid";
|
|
8894
|
+
var MAX_TRANSACTION_ITEMS = 100;
|
|
8895
|
+
var TransactionService = class {
|
|
8896
|
+
constructor(EntityConfig, EmailAuthEnabledEntities, entityRepository, dynamodbClient, publishEvent2, entityServiceLifeCycle, eventUtils) {
|
|
8897
|
+
this.EntityConfig = EntityConfig;
|
|
8898
|
+
this.EmailAuthEnabledEntities = EmailAuthEnabledEntities;
|
|
8899
|
+
this.entityRepository = entityRepository;
|
|
8900
|
+
this.dynamodbClient = dynamodbClient;
|
|
8901
|
+
this.publishEvent = publishEvent2;
|
|
8902
|
+
this.entityServiceLifeCycle = entityServiceLifeCycle;
|
|
8903
|
+
this.eventUtils = eventUtils;
|
|
8904
|
+
this.executeTransaction = (operations, accountId) => __async(this, null, function* () {
|
|
8905
|
+
var _a;
|
|
8906
|
+
if (!operations || operations.length === 0) {
|
|
8907
|
+
throw new StandardError(
|
|
8908
|
+
StandardErrorCode.TRANSACTION_EMPTY,
|
|
8909
|
+
"Transaction must contain at least one operation"
|
|
8910
|
+
);
|
|
8911
|
+
}
|
|
8912
|
+
const allTransactItems = [];
|
|
8913
|
+
const pendingEvents = [];
|
|
8914
|
+
const resultEntries = [];
|
|
8915
|
+
for (const op of operations) {
|
|
8916
|
+
switch (op.operation) {
|
|
8917
|
+
case "createEntity": {
|
|
8918
|
+
const { items, entity } = yield this.buildCreateItems(op);
|
|
8919
|
+
allTransactItems.push(...items);
|
|
8920
|
+
pendingEvents.push(
|
|
8921
|
+
...this.collectCreateEvents(
|
|
8922
|
+
entity,
|
|
8923
|
+
op.payload,
|
|
8924
|
+
accountId
|
|
8925
|
+
)
|
|
8926
|
+
);
|
|
8927
|
+
resultEntries.push({
|
|
8928
|
+
operation: "createEntity",
|
|
8929
|
+
entityType: op.entityType,
|
|
8930
|
+
entityId: entity.entityId,
|
|
8931
|
+
data: entity.data
|
|
8932
|
+
});
|
|
8933
|
+
break;
|
|
8934
|
+
}
|
|
8935
|
+
case "updateEntity": {
|
|
8936
|
+
const { item, updatedAt } = yield this.buildUpdateItem(op);
|
|
8937
|
+
allTransactItems.push(item);
|
|
8938
|
+
pendingEvents.push(
|
|
8939
|
+
...this.collectUpdateEvents(
|
|
8940
|
+
op,
|
|
8941
|
+
updatedAt,
|
|
8942
|
+
accountId
|
|
8943
|
+
)
|
|
8944
|
+
);
|
|
8945
|
+
resultEntries.push({
|
|
8946
|
+
operation: "updateEntity",
|
|
8947
|
+
entityType: op.entityType,
|
|
8948
|
+
entityId: op.entityId
|
|
8949
|
+
});
|
|
8950
|
+
break;
|
|
8951
|
+
}
|
|
8952
|
+
case "adjustEntity": {
|
|
8953
|
+
const { item, updatedAt } = yield this.buildAdjustItem(op);
|
|
8954
|
+
allTransactItems.push(item);
|
|
8955
|
+
pendingEvents.push({
|
|
8956
|
+
event: EVENT.CORE.ENTITY_UPDATED,
|
|
8957
|
+
payload: {
|
|
8958
|
+
entityType: op.entityType,
|
|
8959
|
+
entityId: op.entityId,
|
|
8960
|
+
updatedByAccountId: accountId,
|
|
8961
|
+
publishedAt: updatedAt
|
|
8962
|
+
}
|
|
8963
|
+
});
|
|
8964
|
+
resultEntries.push({
|
|
8965
|
+
operation: "adjustEntity",
|
|
8966
|
+
entityType: op.entityType,
|
|
8967
|
+
entityId: op.entityId
|
|
8968
|
+
});
|
|
8969
|
+
break;
|
|
8970
|
+
}
|
|
8971
|
+
case "deleteEntity": {
|
|
8972
|
+
const item = this.buildDeleteItem(op);
|
|
8973
|
+
allTransactItems.push(item);
|
|
8974
|
+
pendingEvents.push({
|
|
8975
|
+
event: EVENT.CORE.ENTITY_DELETED,
|
|
8976
|
+
payload: {
|
|
8977
|
+
entityType: op.entityType,
|
|
8978
|
+
entityId: op.entityId,
|
|
8979
|
+
deletedByAccountId: accountId
|
|
8980
|
+
}
|
|
8981
|
+
});
|
|
8982
|
+
resultEntries.push({
|
|
8983
|
+
operation: "deleteEntity",
|
|
8984
|
+
entityType: op.entityType,
|
|
8985
|
+
entityId: op.entityId
|
|
8986
|
+
});
|
|
8987
|
+
break;
|
|
8988
|
+
}
|
|
8989
|
+
default:
|
|
8990
|
+
throw new StandardError(
|
|
8991
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
8992
|
+
`Unknown operation: '${op.operation}'`
|
|
8993
|
+
);
|
|
8994
|
+
}
|
|
8995
|
+
}
|
|
8996
|
+
if (allTransactItems.length > MAX_TRANSACTION_ITEMS) {
|
|
8997
|
+
throw new StandardError(
|
|
8998
|
+
StandardErrorCode.TRANSACTION_ITEM_LIMIT_EXCEEDED,
|
|
8999
|
+
`Transaction contains ${allTransactItems.length} items, exceeds limit of ${MAX_TRANSACTION_ITEMS}`
|
|
9000
|
+
);
|
|
9001
|
+
}
|
|
9002
|
+
try {
|
|
9003
|
+
yield this.dynamodbClient.transactWriteItems({
|
|
9004
|
+
TransactItems: allTransactItems
|
|
9005
|
+
});
|
|
9006
|
+
} catch (err) {
|
|
9007
|
+
if (err instanceof TransactionCanceledException5) {
|
|
9008
|
+
throw new StandardError(
|
|
9009
|
+
StandardErrorCode.TRANSACTION_FAILED,
|
|
9010
|
+
"Transaction failed",
|
|
9011
|
+
err,
|
|
9012
|
+
{
|
|
9013
|
+
reasons: (_a = err.CancellationReasons) == null ? void 0 : _a.map((r, i) => ({
|
|
9014
|
+
index: i,
|
|
9015
|
+
code: r.Code,
|
|
9016
|
+
message: r.Message
|
|
9017
|
+
}))
|
|
9018
|
+
}
|
|
9019
|
+
);
|
|
9020
|
+
}
|
|
9021
|
+
throw err;
|
|
9022
|
+
}
|
|
9023
|
+
const readPromises = resultEntries.map((entry) => __async(this, null, function* () {
|
|
9024
|
+
if ((entry.operation === "updateEntity" || entry.operation === "adjustEntity") && !entry.data) {
|
|
9025
|
+
try {
|
|
9026
|
+
const entity = yield this.entityRepository.getEntity(
|
|
9027
|
+
entry.entityType,
|
|
9028
|
+
entry.entityId
|
|
9029
|
+
);
|
|
9030
|
+
entry.data = entity.data;
|
|
9031
|
+
} catch (e) {
|
|
9032
|
+
}
|
|
9033
|
+
}
|
|
9034
|
+
}));
|
|
9035
|
+
yield Promise.all(readPromises);
|
|
9036
|
+
yield Promise.allSettled(pendingEvents.map((ev) => this.publishEvent(ev)));
|
|
9037
|
+
return { results: resultEntries };
|
|
9038
|
+
});
|
|
9039
|
+
}
|
|
9040
|
+
buildCreateItems(op) {
|
|
9041
|
+
return __async(this, null, function* () {
|
|
9042
|
+
const config = this.EntityConfig[op.entityType];
|
|
9043
|
+
if (!config) {
|
|
9044
|
+
throw new StandardError(
|
|
9045
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9046
|
+
`Unknown entity type: '${op.entityType}'`
|
|
9047
|
+
);
|
|
9048
|
+
}
|
|
9049
|
+
const entitySchema = config.createSchema || config.baseSchema;
|
|
9050
|
+
if (!entitySchema) {
|
|
9051
|
+
throw new StandardError(
|
|
9052
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9053
|
+
`No schema defined for entity type: '${op.entityType}'`
|
|
9054
|
+
);
|
|
9055
|
+
}
|
|
9056
|
+
if (config.finalSchema) {
|
|
9057
|
+
config.finalSchema.parse(op.payload);
|
|
9058
|
+
}
|
|
9059
|
+
const parsedPayload = entitySchema.parse(
|
|
9060
|
+
op.payload
|
|
9061
|
+
);
|
|
9062
|
+
const currentDatetime = /* @__PURE__ */ new Date();
|
|
9063
|
+
const entity = new Entity(
|
|
9064
|
+
op.entityType,
|
|
9065
|
+
op.entityId || ulid6(),
|
|
9066
|
+
parsedPayload,
|
|
9067
|
+
currentDatetime,
|
|
9068
|
+
currentDatetime
|
|
9069
|
+
);
|
|
9070
|
+
const uniqueFields = config.uniqueFields || [];
|
|
9071
|
+
const uniqueFieldValues = {};
|
|
9072
|
+
for (const field of uniqueFields) {
|
|
9073
|
+
if (!(field in parsedPayload)) continue;
|
|
9074
|
+
const value = parsedPayload[field];
|
|
9075
|
+
if (typeof value !== "string") {
|
|
9076
|
+
throw new StandardError(
|
|
9077
|
+
StandardErrorCode.INVALID_UNIQUE_VALUE_TYPE,
|
|
9078
|
+
`Invalid type. ${field} is not a 'string'.`
|
|
9079
|
+
);
|
|
9080
|
+
}
|
|
9081
|
+
uniqueFieldValues[field] = value;
|
|
9082
|
+
}
|
|
9083
|
+
const items = this.entityRepository.createEntityTransactItems(entity, {
|
|
9084
|
+
uniqueFieldValues
|
|
9085
|
+
});
|
|
9086
|
+
return { items, entity };
|
|
9087
|
+
});
|
|
9088
|
+
}
|
|
9089
|
+
buildUpdateItem(op) {
|
|
9090
|
+
return __async(this, null, function* () {
|
|
9091
|
+
const config = this.EntityConfig[op.entityType];
|
|
9092
|
+
if (!config) {
|
|
9093
|
+
throw new StandardError(
|
|
9094
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9095
|
+
`Unknown entity type: '${op.entityType}'`
|
|
9096
|
+
);
|
|
9097
|
+
}
|
|
9098
|
+
const uniqueFields = config.uniqueFields || [];
|
|
9099
|
+
for (const field of uniqueFields) {
|
|
9100
|
+
if (field in op.payload) {
|
|
9101
|
+
throw new StandardError(
|
|
9102
|
+
StandardErrorCode.TRANSACTION_UNIQUE_FIELD_UPDATE,
|
|
9103
|
+
`Cannot update unique field '${field}' within a transaction. Use a standalone updateEntity call instead.`
|
|
9104
|
+
);
|
|
9105
|
+
}
|
|
9106
|
+
}
|
|
9107
|
+
const entitySchema = config.baseSchema;
|
|
9108
|
+
if (!entitySchema) {
|
|
9109
|
+
throw new StandardError(
|
|
9110
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9111
|
+
`No schema defined for entity type: '${op.entityType}'`
|
|
9112
|
+
);
|
|
9113
|
+
}
|
|
9114
|
+
const parsedPayload = entitySchema.partial().parse(op.payload);
|
|
9115
|
+
const currentDatetime = (/* @__PURE__ */ new Date()).toISOString();
|
|
9116
|
+
const toUpdateExpressions = this.entityRepository.toUpdate({
|
|
9117
|
+
updatedAt: currentDatetime,
|
|
9118
|
+
data: parsedPayload
|
|
9119
|
+
});
|
|
9120
|
+
let conditionOpts;
|
|
9121
|
+
if (op.condition) {
|
|
9122
|
+
const updateConditions = config.updateConditions;
|
|
9123
|
+
if (!updateConditions) {
|
|
9124
|
+
throw new StandardError(
|
|
9125
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
9126
|
+
`Entity '${op.entityType}' has no updateConditions defined`
|
|
9127
|
+
);
|
|
9128
|
+
}
|
|
9129
|
+
conditionOpts = yield resolveUpdateCondition({
|
|
9130
|
+
conditionName: op.condition,
|
|
9131
|
+
conditions: updateConditions,
|
|
9132
|
+
getEntityData: () => __async(this, null, function* () {
|
|
9133
|
+
var _a;
|
|
9134
|
+
const entity2 = yield this.entityRepository.getEntity(
|
|
9135
|
+
op.entityType,
|
|
9136
|
+
op.entityId
|
|
9137
|
+
);
|
|
9138
|
+
return (_a = entity2 == null ? void 0 : entity2.data) != null ? _a : {};
|
|
9139
|
+
})
|
|
9140
|
+
});
|
|
9141
|
+
}
|
|
9142
|
+
const entity = new Entity(op.entityType, op.entityId);
|
|
9143
|
+
const item = {
|
|
9144
|
+
Update: {
|
|
9145
|
+
TableName: this.entityRepository.TABLE_NAME,
|
|
9146
|
+
Key: entity.keys(),
|
|
9147
|
+
ConditionExpression: (conditionOpts == null ? void 0 : conditionOpts.ConditionExpression) || "attribute_exists(PK)",
|
|
9148
|
+
UpdateExpression: toUpdateExpressions.UpdateExpression,
|
|
9149
|
+
ExpressionAttributeNames: __spreadValues(__spreadValues({}, toUpdateExpressions.ExpressionAttributeNames), conditionOpts == null ? void 0 : conditionOpts.ExpressionAttributeNames),
|
|
9150
|
+
ExpressionAttributeValues: __spreadValues(__spreadValues({}, toUpdateExpressions.ExpressionAttributeValues), conditionOpts == null ? void 0 : conditionOpts.ExpressionAttributeValues)
|
|
9151
|
+
}
|
|
9152
|
+
};
|
|
9153
|
+
return { item, updatedAt: currentDatetime };
|
|
9154
|
+
});
|
|
9155
|
+
}
|
|
9156
|
+
buildAdjustItem(op) {
|
|
9157
|
+
return __async(this, null, function* () {
|
|
9158
|
+
const config = this.EntityConfig[op.entityType];
|
|
9159
|
+
if (!config) {
|
|
9160
|
+
throw new StandardError(
|
|
9161
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9162
|
+
`Unknown entity type: '${op.entityType}'`
|
|
9163
|
+
);
|
|
9164
|
+
}
|
|
9165
|
+
for (const [key, value] of Object.entries(op.adjustments)) {
|
|
9166
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
9167
|
+
throw new StandardError(
|
|
9168
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9169
|
+
`Adjustment field "${key}" must be a finite number`
|
|
9170
|
+
);
|
|
9171
|
+
}
|
|
9172
|
+
}
|
|
9173
|
+
const {
|
|
9174
|
+
UpdateExpression,
|
|
9175
|
+
ExpressionAttributeNames,
|
|
9176
|
+
ExpressionAttributeValues
|
|
9177
|
+
} = this.entityRepository.toAdjustUpdate(op.adjustments);
|
|
9178
|
+
const currentDatetime = (/* @__PURE__ */ new Date()).toISOString();
|
|
9179
|
+
ExpressionAttributeNames["#updatedAt"] = "updatedAt";
|
|
9180
|
+
ExpressionAttributeValues[":updatedAt"] = { S: currentDatetime };
|
|
9181
|
+
const fullUpdateExpression = `${UpdateExpression}, #updatedAt = :updatedAt`;
|
|
9182
|
+
let conditionOpts;
|
|
9183
|
+
const adjustmentConditions = config.adjustmentConditions;
|
|
9184
|
+
if (adjustmentConditions) {
|
|
9185
|
+
if (!op.condition) {
|
|
9186
|
+
throw new StandardError(
|
|
9187
|
+
StandardErrorCode.INVALID_CONDITION,
|
|
9188
|
+
`Entity '${op.entityType}' has adjustmentConditions defined; condition is required`
|
|
9189
|
+
);
|
|
9190
|
+
}
|
|
9191
|
+
conditionOpts = yield resolveAdjustmentCondition({
|
|
9192
|
+
conditionName: op.condition,
|
|
9193
|
+
conditions: adjustmentConditions,
|
|
9194
|
+
adjustments: op.adjustments,
|
|
9195
|
+
getEntityData: () => __async(this, null, function* () {
|
|
9196
|
+
var _a;
|
|
9197
|
+
const entity2 = yield this.entityRepository.getEntity(
|
|
9198
|
+
op.entityType,
|
|
9199
|
+
op.entityId
|
|
9200
|
+
);
|
|
9201
|
+
return (_a = entity2 == null ? void 0 : entity2.data) != null ? _a : {};
|
|
9202
|
+
})
|
|
9203
|
+
});
|
|
9204
|
+
}
|
|
9205
|
+
const entity = new Entity(op.entityType, op.entityId);
|
|
9206
|
+
const item = {
|
|
9207
|
+
Update: {
|
|
9208
|
+
TableName: this.entityRepository.TABLE_NAME,
|
|
9209
|
+
Key: entity.keys(),
|
|
9210
|
+
UpdateExpression: fullUpdateExpression,
|
|
9211
|
+
ConditionExpression: (conditionOpts == null ? void 0 : conditionOpts.ConditionExpression) || "attribute_exists(PK)",
|
|
9212
|
+
ExpressionAttributeNames: __spreadValues(__spreadValues({}, ExpressionAttributeNames), conditionOpts == null ? void 0 : conditionOpts.ExpressionAttributeNames),
|
|
9213
|
+
ExpressionAttributeValues: __spreadValues(__spreadValues({}, ExpressionAttributeValues), conditionOpts == null ? void 0 : conditionOpts.ExpressionAttributeValues)
|
|
9214
|
+
}
|
|
9215
|
+
};
|
|
9216
|
+
return { item, updatedAt: currentDatetime };
|
|
9217
|
+
});
|
|
9218
|
+
}
|
|
9219
|
+
buildDeleteItem(op) {
|
|
9220
|
+
const config = this.EntityConfig[op.entityType];
|
|
9221
|
+
if (!config) {
|
|
9222
|
+
throw new StandardError(
|
|
9223
|
+
StandardErrorCode.INVALID_ENTITY_TYPE,
|
|
9224
|
+
`Unknown entity type: '${op.entityType}'`
|
|
9225
|
+
);
|
|
9226
|
+
}
|
|
9227
|
+
const entity = new Entity(op.entityType, op.entityId);
|
|
9228
|
+
return {
|
|
9229
|
+
Delete: {
|
|
9230
|
+
TableName: this.entityRepository.TABLE_NAME,
|
|
9231
|
+
Key: entity.keys(),
|
|
9232
|
+
ConditionExpression: "attribute_exists(PK)"
|
|
9233
|
+
}
|
|
9234
|
+
};
|
|
9235
|
+
}
|
|
9236
|
+
collectCreateEvents(entity, payload, accountId) {
|
|
9237
|
+
var _a, _b;
|
|
9238
|
+
const events = [];
|
|
9239
|
+
const publishedAt = entity.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
9240
|
+
const config = this.EntityConfig[entity.entityType];
|
|
9241
|
+
const mutualSchema = (_a = config == null ? void 0 : config.mutual) == null ? void 0 : _a.mutualSchema;
|
|
9242
|
+
if (mutualSchema) {
|
|
9243
|
+
const parsedMutualPayload = mutualSchema.parse(payload);
|
|
9244
|
+
if (parsedMutualPayload) {
|
|
9245
|
+
for (const [fieldKey, fieldConfig] of Object.entries(
|
|
9246
|
+
((_b = config.mutual) == null ? void 0 : _b.mutualFields) || {}
|
|
9247
|
+
)) {
|
|
9248
|
+
const toMutualIds = fieldConfig.toMutualIds;
|
|
9249
|
+
const mutualPayload = parsedMutualPayload[fieldKey];
|
|
9250
|
+
if (!mutualPayload) continue;
|
|
9251
|
+
events.push({
|
|
9252
|
+
event: EVENT.CORE.ENTITY_MUTUAL_TO_CREATE,
|
|
9253
|
+
payload: {
|
|
9254
|
+
byEntityType: entity.entityType,
|
|
9255
|
+
byEntityId: entity.entityId,
|
|
9256
|
+
entityType: fieldConfig.entityType,
|
|
9257
|
+
field: fieldKey,
|
|
9258
|
+
mutualIds: toMutualIds ? toMutualIds(mutualPayload) : mutualPayload,
|
|
9259
|
+
customContext: toMutualIds ? mutualPayload : {},
|
|
9260
|
+
publishedAt
|
|
9261
|
+
}
|
|
9262
|
+
});
|
|
9263
|
+
}
|
|
9264
|
+
}
|
|
9265
|
+
}
|
|
9266
|
+
events.push({
|
|
9267
|
+
event: EVENT.CORE.ENTITY_CREATED,
|
|
9268
|
+
payload: {
|
|
9269
|
+
entityType: entity.entityType,
|
|
9270
|
+
entityId: entity.entityId,
|
|
9271
|
+
data: entity.data,
|
|
9272
|
+
createdByAccountId: accountId,
|
|
9273
|
+
publishedAt
|
|
9274
|
+
}
|
|
9275
|
+
});
|
|
9276
|
+
return events;
|
|
9277
|
+
}
|
|
9278
|
+
collectUpdateEvents(op, updatedAt, accountId) {
|
|
9279
|
+
var _a, _b;
|
|
9280
|
+
const events = [];
|
|
9281
|
+
const config = this.EntityConfig[op.entityType];
|
|
9282
|
+
const mutualSchema = (_a = config == null ? void 0 : config.mutual) == null ? void 0 : _a.mutualSchema;
|
|
9283
|
+
if (mutualSchema) {
|
|
9284
|
+
const parsedMutualPayload = mutualSchema.parse(op.payload);
|
|
9285
|
+
if (parsedMutualPayload) {
|
|
9286
|
+
for (const [fieldKey, fieldConfig] of Object.entries(
|
|
9287
|
+
((_b = config.mutual) == null ? void 0 : _b.mutualFields) || {}
|
|
9288
|
+
)) {
|
|
9289
|
+
const toMutualIds = fieldConfig.toMutualIds;
|
|
9290
|
+
const mutualPayload = parsedMutualPayload[fieldKey];
|
|
9291
|
+
if (!mutualPayload) continue;
|
|
9292
|
+
events.push({
|
|
9293
|
+
event: EVENT.CORE.ENTITY_MUTUAL_TO_UPDATE,
|
|
9294
|
+
payload: {
|
|
9295
|
+
byEntityType: op.entityType,
|
|
9296
|
+
byEntityId: op.entityId,
|
|
9297
|
+
entityType: fieldConfig.entityType,
|
|
9298
|
+
field: fieldKey,
|
|
9299
|
+
mutualIds: toMutualIds ? toMutualIds(mutualPayload) : mutualPayload,
|
|
9300
|
+
customContext: toMutualIds ? mutualPayload : {},
|
|
9301
|
+
publishedAt: updatedAt
|
|
9302
|
+
}
|
|
9303
|
+
});
|
|
9304
|
+
}
|
|
9305
|
+
}
|
|
9306
|
+
}
|
|
9307
|
+
events.push({
|
|
9308
|
+
event: EVENT.CORE.ENTITY_UPDATED,
|
|
9309
|
+
payload: {
|
|
9310
|
+
entityType: op.entityType,
|
|
9311
|
+
entityId: op.entityId,
|
|
9312
|
+
updatedByAccountId: accountId,
|
|
9313
|
+
publishedAt: updatedAt
|
|
9314
|
+
}
|
|
9315
|
+
});
|
|
9316
|
+
return events;
|
|
9317
|
+
}
|
|
9318
|
+
};
|
|
9319
|
+
|
|
8002
9320
|
// services/DependencyContainer.ts
|
|
8003
9321
|
var DependencyContainer = class {
|
|
8004
9322
|
constructor(config) {
|
|
@@ -8091,6 +9409,13 @@ var DependencyContainer = class {
|
|
|
8091
9409
|
this.dynamodbClient
|
|
8092
9410
|
);
|
|
8093
9411
|
}
|
|
9412
|
+
get websocketRepository() {
|
|
9413
|
+
return this.createCachedInstance(
|
|
9414
|
+
WebSocketRepository,
|
|
9415
|
+
this.coreTable,
|
|
9416
|
+
this.dynamodbClient
|
|
9417
|
+
);
|
|
9418
|
+
}
|
|
8094
9419
|
get getEntityController() {
|
|
8095
9420
|
return this.createCachedInstance(
|
|
8096
9421
|
GetEntityController,
|
|
@@ -8174,6 +9499,62 @@ var DependencyContainer = class {
|
|
|
8174
9499
|
get listTagsController() {
|
|
8175
9500
|
return this.createCachedInstance(ListTagsController, this.tagRepository);
|
|
8176
9501
|
}
|
|
9502
|
+
get transactionService() {
|
|
9503
|
+
return this.createCachedInstance(
|
|
9504
|
+
TransactionService,
|
|
9505
|
+
this.config.EntityConfig,
|
|
9506
|
+
this.config.EmailAuthEnabledEntities,
|
|
9507
|
+
this.entityRepository,
|
|
9508
|
+
this.dynamodbClient,
|
|
9509
|
+
this.publishEvent,
|
|
9510
|
+
this.entityServiceLifeCycle,
|
|
9511
|
+
this.eventUtils
|
|
9512
|
+
);
|
|
9513
|
+
}
|
|
9514
|
+
get executeTransactionController() {
|
|
9515
|
+
return this.createCachedInstance(
|
|
9516
|
+
ExecuteTransactionController,
|
|
9517
|
+
this.transactionService
|
|
9518
|
+
);
|
|
9519
|
+
}
|
|
9520
|
+
get createTicketController() {
|
|
9521
|
+
return this.createCachedInstance(CreateTicketController, this);
|
|
9522
|
+
}
|
|
9523
|
+
};
|
|
9524
|
+
|
|
9525
|
+
// helpers/transactional.ts
|
|
9526
|
+
var transactional = {
|
|
9527
|
+
createEntity: (entityType, payload) => {
|
|
9528
|
+
const _a = payload, { entityId } = _a, rest = __objRest(_a, ["entityId"]);
|
|
9529
|
+
return __spreadValues({
|
|
9530
|
+
operation: "createEntity",
|
|
9531
|
+
entityType,
|
|
9532
|
+
payload: rest
|
|
9533
|
+
}, entityId && { entityId });
|
|
9534
|
+
},
|
|
9535
|
+
updateEntity: (entityType, entityId, payload) => {
|
|
9536
|
+
const _a = payload, { $condition } = _a, rest = __objRest(_a, ["$condition"]);
|
|
9537
|
+
return __spreadValues({
|
|
9538
|
+
operation: "updateEntity",
|
|
9539
|
+
entityType,
|
|
9540
|
+
entityId,
|
|
9541
|
+
payload: rest
|
|
9542
|
+
}, $condition && { condition: $condition });
|
|
9543
|
+
},
|
|
9544
|
+
adjustEntity: (entityType, entityId, adjustments) => {
|
|
9545
|
+
const _a = adjustments, { $condition } = _a, rest = __objRest(_a, ["$condition"]);
|
|
9546
|
+
return __spreadValues({
|
|
9547
|
+
operation: "adjustEntity",
|
|
9548
|
+
entityType,
|
|
9549
|
+
entityId,
|
|
9550
|
+
adjustments: rest
|
|
9551
|
+
}, $condition && { condition: $condition });
|
|
9552
|
+
},
|
|
9553
|
+
deleteEntity: (entityType, entityId) => ({
|
|
9554
|
+
operation: "deleteEntity",
|
|
9555
|
+
entityType,
|
|
9556
|
+
entityId
|
|
9557
|
+
})
|
|
8177
9558
|
};
|
|
8178
9559
|
|
|
8179
9560
|
// index.ts
|
|
@@ -8189,6 +9570,10 @@ var CoreFactory = class {
|
|
|
8189
9570
|
this.prejoinProcessor = handler3(dependencyContainer);
|
|
8190
9571
|
this.tagProcessor = handler5(dependencyContainer);
|
|
8191
9572
|
this.appHandler = appHandler(dependencyContainer);
|
|
9573
|
+
this.wsConnect = connect(dependencyContainer);
|
|
9574
|
+
this.wsDisconnect = disconnect(dependencyContainer);
|
|
9575
|
+
this.wsDefault = $default(dependencyContainer);
|
|
9576
|
+
this.wsBroadcast = broadcast(dependencyContainer);
|
|
8192
9577
|
}
|
|
8193
9578
|
};
|
|
8194
9579
|
var index_default = CoreFactory;
|
|
@@ -8204,6 +9589,8 @@ export {
|
|
|
8204
9589
|
StandardError,
|
|
8205
9590
|
StandardErrorCode,
|
|
8206
9591
|
TagRepository,
|
|
9592
|
+
TransactionService,
|
|
9593
|
+
WebSocketRepository,
|
|
8207
9594
|
appHandler,
|
|
8208
9595
|
handler as createEntityProcessor,
|
|
8209
9596
|
index_default as default,
|
|
@@ -8211,6 +9598,11 @@ export {
|
|
|
8211
9598
|
handler3 as prejoinProcessor,
|
|
8212
9599
|
handler4 as replicationProcessor,
|
|
8213
9600
|
setupCommonRoutes,
|
|
8214
|
-
handler5 as tagProcessor
|
|
9601
|
+
handler5 as tagProcessor,
|
|
9602
|
+
transactional,
|
|
9603
|
+
broadcast as wsBroadcast,
|
|
9604
|
+
connect as wsConnect,
|
|
9605
|
+
$default as wsDefault,
|
|
9606
|
+
disconnect as wsDisconnect
|
|
8215
9607
|
};
|
|
8216
9608
|
//# sourceMappingURL=index.js.map
|