@getstrata/core 0.5.13 → 0.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -6
- package/dist/bootstrap/contracts.d.ts +2 -0
- package/dist/bootstrap/httpKernel.d.ts +1 -2
- package/dist/core/admin/registry.d.ts +1 -0
- package/dist/core/auth/membershipService.d.ts +5 -1
- package/dist/core/database/baseRepository.d.ts +6 -1
- package/dist/core/database/connectionContext.d.ts +2 -1
- package/dist/core/database/defaultConnection.d.ts +6 -0
- package/dist/core/database/queryProxy.d.ts +3 -0
- package/dist/core/database/repositoryConnection.d.ts +3 -3
- package/dist/core/jobs/dispatchWebhookJob.d.ts +0 -1
- package/dist/core/security/safeFetch.d.ts +2 -0
- package/dist/core/security/safeUrl.d.ts +16 -1
- package/dist/core/storage/storage.d.ts +6 -3
- package/dist/core/tenant/tenantDatabaseScope.d.ts +2 -1
- package/dist/db/connection/index.d.ts +1 -1
- package/dist/entries/auth/accessControl.js +1 -113
- package/dist/entries/auth/authContext.js +1 -15
- package/dist/entries/auth/guard.js +1 -3044
- package/dist/entries/auth/membershipContext.js +1 -276
- package/dist/entries/auth/membershipScope.js +1 -390
- package/dist/entries/auth/membershipService.js +1 -480
- package/dist/entries/auth/policy.js +1 -134
- package/dist/entries/database.js +1 -2398
- package/dist/entries/http/csrfToken.js +0 -6
- package/dist/entries/http/middleware.js +1 -68
- package/dist/entries/http/requestMetaContext.js +1 -18
- package/dist/entries/http/webErrorResponse.js +94 -563
- package/dist/entries/http/webFormRequest.js +0 -131
- package/dist/entries/http.js +1 -4091
- package/dist/entries/jobs/dispatchWebhookJob.js +109 -102
- package/dist/entries/queue/createAppQueue.js +111 -631
- package/dist/entries/queue/jobRunner.js +0 -3
- package/dist/entries/queue/publicQueue.js +45 -546
- package/dist/entries/queue/queueMetrics.js +111 -631
- package/dist/entries/security/safeUrl.js +29 -0
- package/dist/entries/security/securityEvents.js +1 -41
- package/dist/entries/storage/storage.js +14 -4
- package/dist/entries/tenant/tenantContext.js +1 -30
- package/dist/entries/tenant/tenantMiddleware.js +1 -312
- package/dist/entries/tracing/traceContext.js +1 -15
- package/dist/entries/view.js +94 -563
- package/dist/framework/public-api.d.ts +36 -4
- package/dist/index.js +2270 -1666
- package/dist/modules/user/repository.d.ts +1 -0
- package/package.json +6 -5
|
@@ -183,9 +183,6 @@ function isHtmxRequest(request) {
|
|
|
183
183
|
return request.headers.get("HX-Request") === "true";
|
|
184
184
|
}
|
|
185
185
|
// ../../src/bootstrap/config.ts
|
|
186
|
-
var CORE_QUEUE_TOKEN = "core.queue";
|
|
187
|
-
var CORE_POLICY_GATE_TOKEN = "core.policyGate";
|
|
188
|
-
var CORE_AUTH_TOKEN = "core.auth";
|
|
189
186
|
var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
|
|
190
187
|
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
191
188
|
|
|
@@ -494,14 +491,6 @@ function appendWhereParts(tableName, where, params) {
|
|
|
494
491
|
}
|
|
495
492
|
return clauses.join(" AND ");
|
|
496
493
|
}
|
|
497
|
-
function buildWhereClause(tableName, where = {}) {
|
|
498
|
-
const params = [];
|
|
499
|
-
const body = appendWhereParts(tableName, where, params);
|
|
500
|
-
return {
|
|
501
|
-
clause: body.length > 0 ? ` WHERE ${body}` : "",
|
|
502
|
-
params
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
494
|
function buildWhereNodeClause(tableName, node, params) {
|
|
506
495
|
if ("where" in node) {
|
|
507
496
|
return appendWhereParts(tableName, node.where, params);
|
|
@@ -804,30 +793,6 @@ function buildDeleteByIdQuery(table, id) {
|
|
|
804
793
|
}
|
|
805
794
|
|
|
806
795
|
// ../../src/core/database/relationships.ts
|
|
807
|
-
function hasMany(definition) {
|
|
808
|
-
return {
|
|
809
|
-
type: "hasMany",
|
|
810
|
-
...definition
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
function hasOne(definition) {
|
|
814
|
-
return {
|
|
815
|
-
type: "hasOne",
|
|
816
|
-
...definition
|
|
817
|
-
};
|
|
818
|
-
}
|
|
819
|
-
function belongsTo(definition) {
|
|
820
|
-
return {
|
|
821
|
-
type: "belongsTo",
|
|
822
|
-
...definition
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
function belongsToMany(definition) {
|
|
826
|
-
return {
|
|
827
|
-
type: "belongsToMany",
|
|
828
|
-
...definition
|
|
829
|
-
};
|
|
830
|
-
}
|
|
831
796
|
function indexHasManyRelation(parents, children, relation) {
|
|
832
797
|
const groups = new Map;
|
|
833
798
|
for (const parent of parents) {
|
|
@@ -843,15 +808,6 @@ function indexHasManyRelation(parents, children, relation) {
|
|
|
843
808
|
}
|
|
844
809
|
return groups;
|
|
845
810
|
}
|
|
846
|
-
function indexHasOneRelation(parents, children, relation) {
|
|
847
|
-
const grouped = indexHasManyRelation(parents, children, relation);
|
|
848
|
-
const result = new Map;
|
|
849
|
-
for (const parent of parents) {
|
|
850
|
-
const matches = grouped.get(parent[relation.localKey]) ?? [];
|
|
851
|
-
result.set(parent[relation.localKey], matches[0]);
|
|
852
|
-
}
|
|
853
|
-
return result;
|
|
854
|
-
}
|
|
855
811
|
function indexBelongsToRelation(children, parents, relation) {
|
|
856
812
|
const parentsById = new Map;
|
|
857
813
|
for (const parent of parents) {
|
|
@@ -867,45 +823,6 @@ function indexBelongsToRelation(children, parents, relation) {
|
|
|
867
823
|
}
|
|
868
824
|
return result;
|
|
869
825
|
}
|
|
870
|
-
function indexBelongsToManyRelation(parents, pivotRows, relatedRows, relation) {
|
|
871
|
-
const relatedById = new Map;
|
|
872
|
-
for (const related of relatedRows) {
|
|
873
|
-
relatedById.set(related[relation.relatedKey], related);
|
|
874
|
-
}
|
|
875
|
-
const groups = new Map;
|
|
876
|
-
for (const parent of parents) {
|
|
877
|
-
groups.set(parent[relation.parentKey], []);
|
|
878
|
-
}
|
|
879
|
-
for (const pivot of pivotRows) {
|
|
880
|
-
const parentId = pivot[relation.foreignPivotKey];
|
|
881
|
-
const relatedId = pivot[relation.relatedPivotKey];
|
|
882
|
-
const group = groups.get(parentId);
|
|
883
|
-
const related = relatedById.get(relatedId);
|
|
884
|
-
if (!group || !related) {
|
|
885
|
-
continue;
|
|
886
|
-
}
|
|
887
|
-
group.push(related);
|
|
888
|
-
}
|
|
889
|
-
return groups;
|
|
890
|
-
}
|
|
891
|
-
function morphMany(definition) {
|
|
892
|
-
return {
|
|
893
|
-
type: "morphMany",
|
|
894
|
-
...definition
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
function morphOne(definition) {
|
|
898
|
-
return {
|
|
899
|
-
type: "morphOne",
|
|
900
|
-
...definition
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
function morphTo(definition) {
|
|
904
|
-
return {
|
|
905
|
-
type: "morphTo",
|
|
906
|
-
...definition
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
826
|
function indexMorphManyRelation(parents, children, relation) {
|
|
910
827
|
const groups = new Map;
|
|
911
828
|
for (const parent of parents) {
|
|
@@ -924,15 +841,6 @@ function indexMorphManyRelation(parents, children, relation) {
|
|
|
924
841
|
}
|
|
925
842
|
return groups;
|
|
926
843
|
}
|
|
927
|
-
function indexMorphOneRelation(parents, children, relation) {
|
|
928
|
-
const grouped = indexMorphManyRelation(parents, children, relation);
|
|
929
|
-
const result = new Map;
|
|
930
|
-
for (const parent of parents) {
|
|
931
|
-
const matches = grouped.get(parent[relation.localKey]) ?? [];
|
|
932
|
-
result.set(parent[relation.localKey], matches[0]);
|
|
933
|
-
}
|
|
934
|
-
return result;
|
|
935
|
-
}
|
|
936
844
|
function indexMorphToRelation(children, parentsByType, relation) {
|
|
937
845
|
const result = new Map;
|
|
938
846
|
for (const child of children) {
|
|
@@ -949,92 +857,71 @@ function indexMorphToRelation(children, parentsByType, relation) {
|
|
|
949
857
|
return result;
|
|
950
858
|
}
|
|
951
859
|
|
|
952
|
-
// ../../src/
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
956
|
-
}
|
|
957
|
-
var databaseConfig = {
|
|
958
|
-
url: process.env.DATABASE_URL ?? "",
|
|
959
|
-
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
960
|
-
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
961
|
-
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
962
|
-
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
860
|
+
// ../../src/core/database/boundConnection.ts
|
|
861
|
+
var boundConnectionHolder = {
|
|
862
|
+
connection: null
|
|
963
863
|
};
|
|
864
|
+
function getBoundDatabaseConnection() {
|
|
865
|
+
return boundConnectionHolder.connection;
|
|
866
|
+
}
|
|
964
867
|
|
|
965
868
|
// ../../src/core/database/connectionContext.ts
|
|
966
869
|
import { AsyncLocalStorage } from "async_hooks";
|
|
967
870
|
var activeConnection = new AsyncLocalStorage;
|
|
968
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
969
|
-
return activeConnection.run(connection, callback);
|
|
970
|
-
}
|
|
971
871
|
function getActiveDatabaseConnection(fallback) {
|
|
972
872
|
return activeConnection.getStore() ?? fallback;
|
|
973
873
|
}
|
|
974
874
|
|
|
975
|
-
// ../../src/
|
|
976
|
-
var
|
|
977
|
-
function
|
|
978
|
-
|
|
979
|
-
|
|
875
|
+
// ../../src/core/database/queryProxy.ts
|
|
876
|
+
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
877
|
+
function createDatabaseQueryProxy(pool) {
|
|
878
|
+
function resolveDatabase() {
|
|
879
|
+
return getActiveDatabaseConnection(pool);
|
|
980
880
|
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
881
|
+
function resolveDatabaseForProperty(property) {
|
|
882
|
+
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
883
|
+
return pool;
|
|
884
|
+
}
|
|
885
|
+
return resolveDatabase();
|
|
886
|
+
}
|
|
887
|
+
return new Proxy(function database() {}, {
|
|
888
|
+
apply(_target, _thisArg, args) {
|
|
889
|
+
return resolveDatabase()(...args);
|
|
890
|
+
},
|
|
891
|
+
get(_target, property) {
|
|
892
|
+
const connection = resolveDatabaseForProperty(property);
|
|
893
|
+
const value = connection[property];
|
|
894
|
+
return typeof value === "function" ? value.bind(connection) : value;
|
|
895
|
+
}
|
|
987
896
|
});
|
|
988
897
|
}
|
|
989
898
|
|
|
990
|
-
// ../../src/
|
|
991
|
-
var
|
|
899
|
+
// ../../src/core/database/defaultConnection.ts
|
|
900
|
+
var defaultPool = {
|
|
992
901
|
connection: null
|
|
993
902
|
};
|
|
994
|
-
|
|
995
|
-
if (!connectionHolder.connection) {
|
|
996
|
-
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
997
|
-
}
|
|
998
|
-
return connectionHolder.connection;
|
|
999
|
-
}
|
|
1000
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
1001
|
-
function resolveDatabase() {
|
|
1002
|
-
return getActiveDatabaseConnection(getDatabase());
|
|
1003
|
-
}
|
|
1004
|
-
function resolveDatabaseForProperty(property) {
|
|
1005
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
1006
|
-
return getDatabase();
|
|
1007
|
-
}
|
|
1008
|
-
return resolveDatabase();
|
|
1009
|
-
}
|
|
1010
|
-
var db = new Proxy(function database() {}, {
|
|
1011
|
-
apply(_target, _thisArg, args) {
|
|
1012
|
-
return resolveDatabase()(...args);
|
|
1013
|
-
},
|
|
1014
|
-
get(_target, property) {
|
|
1015
|
-
const connection = resolveDatabaseForProperty(property);
|
|
1016
|
-
const value = connection[property];
|
|
1017
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
1018
|
-
}
|
|
1019
|
-
});
|
|
1020
|
-
var connection_default = db;
|
|
1021
|
-
|
|
1022
|
-
// ../../src/core/database/boundConnection.ts
|
|
1023
|
-
var boundConnectionHolder = {
|
|
903
|
+
var defaultQuery = {
|
|
1024
904
|
connection: null
|
|
1025
905
|
};
|
|
1026
|
-
function
|
|
1027
|
-
|
|
906
|
+
function registerDefaultDatabasePool(connection) {
|
|
907
|
+
defaultPool.connection = connection;
|
|
908
|
+
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
1028
909
|
}
|
|
1029
|
-
function
|
|
1030
|
-
|
|
910
|
+
function getDefaultDatabaseQuery() {
|
|
911
|
+
if (!defaultQuery.connection) {
|
|
912
|
+
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
913
|
+
}
|
|
914
|
+
return defaultQuery.connection;
|
|
1031
915
|
}
|
|
1032
916
|
|
|
1033
917
|
// ../../src/core/database/repositoryConnection.ts
|
|
1034
918
|
function resolveRepositoryConnection() {
|
|
1035
|
-
return getBoundDatabaseConnection() ??
|
|
919
|
+
return getBoundDatabaseConnection() ?? getDefaultDatabaseQuery();
|
|
1036
920
|
}
|
|
1037
|
-
var repositoryConnection = new Proxy({}, {
|
|
921
|
+
var repositoryConnection = new Proxy(function repositoryConnection2() {}, {
|
|
922
|
+
apply(_target, _thisArg, args) {
|
|
923
|
+
return resolveRepositoryConnection()(...args);
|
|
924
|
+
},
|
|
1038
925
|
get(_target, property) {
|
|
1039
926
|
const connection = resolveRepositoryConnection();
|
|
1040
927
|
const value = connection[property];
|
|
@@ -1597,327 +1484,10 @@ class BaseRepository {
|
|
|
1597
1484
|
}
|
|
1598
1485
|
}
|
|
1599
1486
|
var baseRepository_default = BaseRepository;
|
|
1600
|
-
// ../../src/core/database/connection.ts
|
|
1601
|
-
function createDatabaseConnection2(source) {
|
|
1602
|
-
return {
|
|
1603
|
-
async unsafe(query, params = []) {
|
|
1604
|
-
return await source.unsafe(query, params);
|
|
1605
|
-
}
|
|
1606
|
-
};
|
|
1607
|
-
}
|
|
1608
1487
|
// ../../src/core/database/model.ts
|
|
1609
1488
|
var modelRepositories = new WeakMap;
|
|
1610
1489
|
var modelGlobalScopes = new WeakMap;
|
|
1611
1490
|
var modelBooted = new WeakSet;
|
|
1612
|
-
function resolveModelRepository(model) {
|
|
1613
|
-
const repository = modelRepositories.get(model);
|
|
1614
|
-
if (!repository) {
|
|
1615
|
-
throw new Error(`${model.name}.repository() is not implemented.`);
|
|
1616
|
-
}
|
|
1617
|
-
return repository;
|
|
1618
|
-
}
|
|
1619
|
-
function modelStatics(model) {
|
|
1620
|
-
return model;
|
|
1621
|
-
}
|
|
1622
|
-
function ensureBooted(model) {
|
|
1623
|
-
if (modelBooted.has(model)) {
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
|
-
modelBooted.add(model);
|
|
1627
|
-
const boot = model.boot;
|
|
1628
|
-
if (typeof boot === "function") {
|
|
1629
|
-
boot.call(model);
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
function getGlobalScopes(model) {
|
|
1633
|
-
return modelGlobalScopes.get(model) ?? [];
|
|
1634
|
-
}
|
|
1635
|
-
function hydrateValue(value, cast) {
|
|
1636
|
-
if (value === null || value === undefined) {
|
|
1637
|
-
return value;
|
|
1638
|
-
}
|
|
1639
|
-
switch (cast) {
|
|
1640
|
-
case "date":
|
|
1641
|
-
case "datetime":
|
|
1642
|
-
return value instanceof Date ? value : new Date(String(value));
|
|
1643
|
-
case "json":
|
|
1644
|
-
return typeof value === "string" ? JSON.parse(value) : value;
|
|
1645
|
-
case "bool":
|
|
1646
|
-
case "boolean":
|
|
1647
|
-
return value === true || value === 1 || value === "1" || value === "true";
|
|
1648
|
-
default:
|
|
1649
|
-
return value;
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
function dehydrateValue(value, cast) {
|
|
1653
|
-
if (value === null || value === undefined) {
|
|
1654
|
-
return value;
|
|
1655
|
-
}
|
|
1656
|
-
switch (cast) {
|
|
1657
|
-
case "date":
|
|
1658
|
-
case "datetime":
|
|
1659
|
-
return value instanceof Date ? value : new Date(String(value));
|
|
1660
|
-
case "json":
|
|
1661
|
-
return typeof value === "string" ? value : JSON.stringify(value);
|
|
1662
|
-
case "bool":
|
|
1663
|
-
case "boolean":
|
|
1664
|
-
return Boolean(value);
|
|
1665
|
-
default:
|
|
1666
|
-
return value;
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
|
-
function filterMassAssignable(fillable, guarded, input) {
|
|
1670
|
-
const resolvedGuarded = guarded ?? true;
|
|
1671
|
-
if (fillable && fillable.length > 0) {
|
|
1672
|
-
const allowed = new Set(fillable);
|
|
1673
|
-
return Object.fromEntries(Object.entries(input).filter(([key]) => allowed.has(key)));
|
|
1674
|
-
}
|
|
1675
|
-
if (resolvedGuarded === true || resolvedGuarded.includes("*")) {
|
|
1676
|
-
return {};
|
|
1677
|
-
}
|
|
1678
|
-
const blocked = new Set(resolvedGuarded);
|
|
1679
|
-
return Object.fromEntries(Object.entries(input).filter(([key]) => !blocked.has(key)));
|
|
1680
|
-
}
|
|
1681
|
-
function applyCasts(values, casts, direction) {
|
|
1682
|
-
if (Object.keys(casts).length === 0) {
|
|
1683
|
-
return values;
|
|
1684
|
-
}
|
|
1685
|
-
const result = { ...values };
|
|
1686
|
-
const castFn = direction === "hydrate" ? hydrateValue : dehydrateValue;
|
|
1687
|
-
for (const [key, cast] of Object.entries(casts)) {
|
|
1688
|
-
if (key in result && cast) {
|
|
1689
|
-
result[key] = castFn(result[key], cast);
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
return result;
|
|
1693
|
-
}
|
|
1694
|
-
function applyTimestampsOnCreate(columns, values, enabled) {
|
|
1695
|
-
if (!enabled) {
|
|
1696
|
-
return values;
|
|
1697
|
-
}
|
|
1698
|
-
const now = new Date;
|
|
1699
|
-
const result = { ...values };
|
|
1700
|
-
if (columns.includes("created_at")) {
|
|
1701
|
-
result.created_at = now;
|
|
1702
|
-
}
|
|
1703
|
-
if (columns.includes("updated_at")) {
|
|
1704
|
-
result.updated_at = now;
|
|
1705
|
-
}
|
|
1706
|
-
return result;
|
|
1707
|
-
}
|
|
1708
|
-
function applyTimestampsOnUpdate(columns, values, enabled) {
|
|
1709
|
-
if (!enabled) {
|
|
1710
|
-
return values;
|
|
1711
|
-
}
|
|
1712
|
-
const result = { ...values };
|
|
1713
|
-
if (columns.includes("updated_at")) {
|
|
1714
|
-
result.updated_at = new Date;
|
|
1715
|
-
}
|
|
1716
|
-
return result;
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
class Model {
|
|
1720
|
-
attributes;
|
|
1721
|
-
repository;
|
|
1722
|
-
static $fillable;
|
|
1723
|
-
static $guarded;
|
|
1724
|
-
static $casts = {};
|
|
1725
|
-
static $timestamps = true;
|
|
1726
|
-
_exists;
|
|
1727
|
-
constructor(attributes, repository, exists = true) {
|
|
1728
|
-
this.attributes = attributes;
|
|
1729
|
-
this.repository = repository;
|
|
1730
|
-
this._exists = exists;
|
|
1731
|
-
}
|
|
1732
|
-
get $exists() {
|
|
1733
|
-
return this._exists;
|
|
1734
|
-
}
|
|
1735
|
-
get(key) {
|
|
1736
|
-
return this.attributes[key];
|
|
1737
|
-
}
|
|
1738
|
-
get id() {
|
|
1739
|
-
return this.attributes[this.primaryKey()];
|
|
1740
|
-
}
|
|
1741
|
-
toObject() {
|
|
1742
|
-
return { ...this.attributes };
|
|
1743
|
-
}
|
|
1744
|
-
primaryKey() {
|
|
1745
|
-
throw new Error(`${this.constructor.name}.primaryKey() is not implemented.`);
|
|
1746
|
-
}
|
|
1747
|
-
static primaryKeyField() {
|
|
1748
|
-
return resolveModelRepository(this).getTable().primaryKey;
|
|
1749
|
-
}
|
|
1750
|
-
static hydrateAttributes(attributes) {
|
|
1751
|
-
const casts = modelStatics(this).$casts ?? {};
|
|
1752
|
-
return applyCasts(attributes, casts, "hydrate");
|
|
1753
|
-
}
|
|
1754
|
-
static dehydrateAttributes(attributes) {
|
|
1755
|
-
const casts = modelStatics(this).$casts ?? {};
|
|
1756
|
-
return applyCasts(attributes, casts, "dehydrate");
|
|
1757
|
-
}
|
|
1758
|
-
static fromRecord(record, repository, exists = true) {
|
|
1759
|
-
const statics = modelStatics(this);
|
|
1760
|
-
const hydrated = statics.hydrateAttributes(record);
|
|
1761
|
-
return new statics(hydrated, repository, exists);
|
|
1762
|
-
}
|
|
1763
|
-
static boot() {}
|
|
1764
|
-
static addGlobalScope(_name, scope) {
|
|
1765
|
-
ensureBooted(this);
|
|
1766
|
-
const existing = modelGlobalScopes.get(this) ?? [];
|
|
1767
|
-
modelGlobalScopes.set(this, [
|
|
1768
|
-
...existing,
|
|
1769
|
-
scope
|
|
1770
|
-
]);
|
|
1771
|
-
}
|
|
1772
|
-
static repository() {
|
|
1773
|
-
return resolveModelRepository(this);
|
|
1774
|
-
}
|
|
1775
|
-
static query() {
|
|
1776
|
-
ensureBooted(this);
|
|
1777
|
-
const repository = resolveModelRepository(this);
|
|
1778
|
-
let query = repository.query();
|
|
1779
|
-
for (const scope of getGlobalScopes(this)) {
|
|
1780
|
-
query = scope(query);
|
|
1781
|
-
}
|
|
1782
|
-
return query;
|
|
1783
|
-
}
|
|
1784
|
-
static async create(attributes) {
|
|
1785
|
-
const statics = modelStatics(this);
|
|
1786
|
-
ensureBooted(this);
|
|
1787
|
-
const repository = resolveModelRepository(this);
|
|
1788
|
-
const table = repository.getTable();
|
|
1789
|
-
const timestamps = statics.$timestamps ?? true;
|
|
1790
|
-
const assignable = filterMassAssignable(statics.$fillable, statics.$guarded, attributes);
|
|
1791
|
-
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
1792
|
-
const payload = statics.dehydrateAttributes(withTimestamps);
|
|
1793
|
-
const record = await repository.create(payload);
|
|
1794
|
-
return statics.fromRecord(record, repository, true);
|
|
1795
|
-
}
|
|
1796
|
-
static async find(id) {
|
|
1797
|
-
const statics = modelStatics(this);
|
|
1798
|
-
const repository = resolveModelRepository(this);
|
|
1799
|
-
const primaryKey = repository.getTable().primaryKey;
|
|
1800
|
-
const record = await Model.query.call(this).where({ [primaryKey]: id }).first();
|
|
1801
|
-
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1802
|
-
}
|
|
1803
|
-
static async findOrFail(id, errorFactory) {
|
|
1804
|
-
const model = await Model.find.call(this, id);
|
|
1805
|
-
if (model) {
|
|
1806
|
-
return model;
|
|
1807
|
-
}
|
|
1808
|
-
throw errorFactory?.(id) ?? new NotFoundError(`${this.name} ${String(id)} not found.`);
|
|
1809
|
-
}
|
|
1810
|
-
static async all(options = {}) {
|
|
1811
|
-
const statics = modelStatics(this);
|
|
1812
|
-
const repository = resolveModelRepository(this);
|
|
1813
|
-
let query = Model.query.call(this);
|
|
1814
|
-
if (options.orderBy) {
|
|
1815
|
-
query = query.orderBy(options.orderBy);
|
|
1816
|
-
}
|
|
1817
|
-
if (options.limit !== undefined) {
|
|
1818
|
-
query = query.limit(options.limit);
|
|
1819
|
-
}
|
|
1820
|
-
const rows = await query.get();
|
|
1821
|
-
return rows.map((row) => statics.fromRecord(row, repository, true));
|
|
1822
|
-
}
|
|
1823
|
-
static async firstWhere(where, options = {}) {
|
|
1824
|
-
const statics = modelStatics(this);
|
|
1825
|
-
const repository = resolveModelRepository(this);
|
|
1826
|
-
let query = Model.query.call(this).where(where);
|
|
1827
|
-
if (options.orderBy) {
|
|
1828
|
-
query = query.orderBy(options.orderBy);
|
|
1829
|
-
}
|
|
1830
|
-
const record = await query.first();
|
|
1831
|
-
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1832
|
-
}
|
|
1833
|
-
async save() {
|
|
1834
|
-
const ModelClass = modelStatics(this.constructor);
|
|
1835
|
-
const timestamps = ModelClass.$timestamps ?? true;
|
|
1836
|
-
const casts = ModelClass.$casts ?? {};
|
|
1837
|
-
const table = this.repository.getTable();
|
|
1838
|
-
if (this.$exists) {
|
|
1839
|
-
const changes = applyTimestampsOnUpdate(table.columns, applyCasts(this.attributes, casts, "dehydrate"), timestamps);
|
|
1840
|
-
const record2 = await this.repository.updateByIdOrThrow(this.id, changes);
|
|
1841
|
-
this.attributes = ModelClass.hydrateAttributes(record2);
|
|
1842
|
-
return this;
|
|
1843
|
-
}
|
|
1844
|
-
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
|
|
1845
|
-
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
1846
|
-
const payload = ModelClass.dehydrateAttributes(withTimestamps);
|
|
1847
|
-
const record = await this.repository.create(payload);
|
|
1848
|
-
this.attributes = ModelClass.hydrateAttributes(record);
|
|
1849
|
-
this._exists = true;
|
|
1850
|
-
return this;
|
|
1851
|
-
}
|
|
1852
|
-
async update(changes) {
|
|
1853
|
-
const ModelClass = modelStatics(this.constructor);
|
|
1854
|
-
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, changes);
|
|
1855
|
-
Object.assign(this.attributes, assignable);
|
|
1856
|
-
return await this.save();
|
|
1857
|
-
}
|
|
1858
|
-
async delete() {
|
|
1859
|
-
if (resolveSoftDeleteColumn(this.repository.getTable())) {
|
|
1860
|
-
return await this.repository.deleteById(this.id);
|
|
1861
|
-
}
|
|
1862
|
-
return await this.repository.forceDeleteById(this.id);
|
|
1863
|
-
}
|
|
1864
|
-
async forceDelete() {
|
|
1865
|
-
return await this.repository.forceDeleteById(this.id);
|
|
1866
|
-
}
|
|
1867
|
-
async restore() {
|
|
1868
|
-
const ModelClass = modelStatics(this.constructor);
|
|
1869
|
-
const record = await this.repository.restoreById(this.id);
|
|
1870
|
-
if (!record) {
|
|
1871
|
-
return null;
|
|
1872
|
-
}
|
|
1873
|
-
this.attributes = ModelClass.hydrateAttributes(record);
|
|
1874
|
-
return this;
|
|
1875
|
-
}
|
|
1876
|
-
async loadHasMany(as, relation, childRepository, options = {}) {
|
|
1877
|
-
const grouped = await childRepository.withConnection(this.repository.getConnection()).loadHasManyForParents([this.attributes], relation, options);
|
|
1878
|
-
const loaded = grouped.get(this.attributes[relation.localKey]) ?? [];
|
|
1879
|
-
return Object.assign(this, { [as]: loaded });
|
|
1880
|
-
}
|
|
1881
|
-
async loadHasOne(as, relation, childRepository, options = {}) {
|
|
1882
|
-
const loaded = await this.loadHasMany(as, relation, childRepository, { ...options, limit: 1 });
|
|
1883
|
-
const value = loaded[as]?.[0];
|
|
1884
|
-
return Object.assign(this, { [as]: value });
|
|
1885
|
-
}
|
|
1886
|
-
async loadBelongsTo(as, relation, parentRepository, options = {}) {
|
|
1887
|
-
const grouped = await this.repository.loadBelongsToForParents([this.attributes], relation, parentRepository, options);
|
|
1888
|
-
const loaded = grouped.get(this.attributes[relation.foreignKey]);
|
|
1889
|
-
return Object.assign(this, { [as]: loaded });
|
|
1890
|
-
}
|
|
1891
|
-
async loadBelongsToMany(as, relation, relatedRepository, options = {}) {
|
|
1892
|
-
const connection = this.repository.getConnection();
|
|
1893
|
-
const parentId = this.attributes[relation.parentKey];
|
|
1894
|
-
const pivotRows = await connection.unsafe(`SELECT * FROM ${relation.pivotTable} WHERE ${String(relation.foreignPivotKey)} = $1`, [parentId]);
|
|
1895
|
-
if (pivotRows.length === 0) {
|
|
1896
|
-
return Object.assign(this, { [as]: [] });
|
|
1897
|
-
}
|
|
1898
|
-
const relatedIds = [
|
|
1899
|
-
...new Set(pivotRows.map((row) => row[relation.relatedPivotKey]))
|
|
1900
|
-
];
|
|
1901
|
-
const relatedRows = await relatedRepository.withConnection(connection).findAll({
|
|
1902
|
-
...options,
|
|
1903
|
-
where: {
|
|
1904
|
-
[relation.relatedKey]: relatedIds
|
|
1905
|
-
}
|
|
1906
|
-
});
|
|
1907
|
-
const grouped = indexBelongsToManyRelation([this.attributes], pivotRows, relatedRows, relation);
|
|
1908
|
-
const loaded = grouped.get(parentId) ?? [];
|
|
1909
|
-
return Object.assign(this, { [as]: loaded });
|
|
1910
|
-
}
|
|
1911
|
-
mergeAttributes(patch) {
|
|
1912
|
-
Object.assign(this.attributes, patch);
|
|
1913
|
-
return this;
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
function registerModelRepository(model, repository) {
|
|
1917
|
-
modelRepositories.set(model, repository);
|
|
1918
|
-
ensureBooted(model);
|
|
1919
|
-
return model;
|
|
1920
|
-
}
|
|
1921
1491
|
// ../../src/core/database/schema/columnDefinition.ts
|
|
1922
1492
|
class ColumnDefinition {
|
|
1923
1493
|
name;
|
|
@@ -2139,45 +1709,6 @@ class Blueprint {
|
|
|
2139
1709
|
});
|
|
2140
1710
|
}
|
|
2141
1711
|
}
|
|
2142
|
-
// ../../src/core/database/schema/driver.ts
|
|
2143
|
-
function normalizeConnectionName(connection) {
|
|
2144
|
-
const normalized = connection.trim().toLowerCase();
|
|
2145
|
-
if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
|
|
2146
|
-
return "pgsql";
|
|
2147
|
-
}
|
|
2148
|
-
if (normalized === "mysql" || normalized === "mariadb") {
|
|
2149
|
-
return "mysql";
|
|
2150
|
-
}
|
|
2151
|
-
if (normalized === "sqlite") {
|
|
2152
|
-
return "sqlite";
|
|
2153
|
-
}
|
|
2154
|
-
throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
|
|
2155
|
-
}
|
|
2156
|
-
function resolveDriverFromUrl(url) {
|
|
2157
|
-
const normalized = url.trim().toLowerCase();
|
|
2158
|
-
if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
|
|
2159
|
-
return "pgsql";
|
|
2160
|
-
}
|
|
2161
|
-
if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
|
|
2162
|
-
return "mysql";
|
|
2163
|
-
}
|
|
2164
|
-
if (normalized.startsWith("sqlite:")) {
|
|
2165
|
-
return "sqlite";
|
|
2166
|
-
}
|
|
2167
|
-
return null;
|
|
2168
|
-
}
|
|
2169
|
-
function resolveDatabaseDriver(options = {}) {
|
|
2170
|
-
const connection = options.connection ?? process.env.DB_CONNECTION;
|
|
2171
|
-
if (connection) {
|
|
2172
|
-
return normalizeConnectionName(connection);
|
|
2173
|
-
}
|
|
2174
|
-
const url = options.url ?? process.env.DATABASE_URL ?? "";
|
|
2175
|
-
const fromUrl = resolveDriverFromUrl(url);
|
|
2176
|
-
if (fromUrl) {
|
|
2177
|
-
return fromUrl;
|
|
2178
|
-
}
|
|
2179
|
-
return "pgsql";
|
|
2180
|
-
}
|
|
2181
1712
|
// ../../src/core/database/schema/errors.ts
|
|
2182
1713
|
class UnsupportedSchemaFeatureError extends Error {
|
|
2183
1714
|
constructor(feature, driver) {
|
|
@@ -2516,48 +2047,70 @@ class SchemaBuilder {
|
|
|
2516
2047
|
toSql() {
|
|
2517
2048
|
return [...this.#statements];
|
|
2518
2049
|
}
|
|
2519
|
-
async execute(
|
|
2050
|
+
async execute(db) {
|
|
2520
2051
|
for (const statement of this.#statements) {
|
|
2521
|
-
await
|
|
2052
|
+
await db.unsafe(statement);
|
|
2522
2053
|
}
|
|
2523
2054
|
}
|
|
2524
2055
|
}
|
|
2525
|
-
|
|
2526
|
-
class Schema {
|
|
2527
|
-
static builder(driver) {
|
|
2528
|
-
return new SchemaBuilder(driver ?? resolveDatabaseDriver());
|
|
2529
|
-
}
|
|
2530
|
-
static async run(db2, driver, callback) {
|
|
2531
|
-
const schema = Schema.builder(driver);
|
|
2532
|
-
await callback(schema);
|
|
2533
|
-
await schema.execute(db2);
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
function createSchemaBuilder(db2, driver) {
|
|
2537
|
-
const builder = Schema.builder(driver);
|
|
2538
|
-
return Object.assign(builder, {
|
|
2539
|
-
async commit() {
|
|
2540
|
-
await builder.execute(db2);
|
|
2541
|
-
}
|
|
2542
|
-
});
|
|
2543
|
-
}
|
|
2544
2056
|
// ../../src/core/database/table.ts
|
|
2545
2057
|
function defineTable(definition) {
|
|
2546
2058
|
return definition;
|
|
2547
2059
|
}
|
|
2548
|
-
// ../../src/
|
|
2549
|
-
function
|
|
2550
|
-
|
|
2060
|
+
// ../../src/config/database.ts
|
|
2061
|
+
function readInteger(name, fallback) {
|
|
2062
|
+
const parsed = Number.parseInt(process.env[name] ?? String(fallback), 10);
|
|
2063
|
+
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
2551
2064
|
}
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2065
|
+
var databaseConfig = {
|
|
2066
|
+
url: process.env.DATABASE_URL ?? "",
|
|
2067
|
+
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
2068
|
+
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
2069
|
+
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
2070
|
+
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
2071
|
+
};
|
|
2072
|
+
|
|
2073
|
+
// ../../src/db/connection/createConnection.ts
|
|
2074
|
+
var {SQL } = globalThis.Bun;
|
|
2075
|
+
function createDatabaseConnection2(config) {
|
|
2076
|
+
if (!config.url) {
|
|
2077
|
+
throw new Error("DATABASE_URL is not configured. Set DATABASE_URL before starting the app or running integration tests.");
|
|
2556
2078
|
}
|
|
2557
|
-
return
|
|
2558
|
-
|
|
2079
|
+
return new SQL({
|
|
2080
|
+
url: config.url,
|
|
2081
|
+
max: config.poolMax,
|
|
2082
|
+
idleTimeout: config.idleTimeoutSeconds,
|
|
2083
|
+
maxLifetime: config.maxLifetimeSeconds,
|
|
2084
|
+
connectionTimeout: config.connectionTimeoutSeconds
|
|
2559
2085
|
});
|
|
2560
2086
|
}
|
|
2087
|
+
|
|
2088
|
+
// ../../src/db/connection/index.ts
|
|
2089
|
+
var connectionHolder = {
|
|
2090
|
+
connection: null
|
|
2091
|
+
};
|
|
2092
|
+
function getDatabase() {
|
|
2093
|
+
if (!connectionHolder.connection) {
|
|
2094
|
+
connectionHolder.connection = createDatabaseConnection2(databaseConfig);
|
|
2095
|
+
registerDefaultDatabasePool(connectionHolder.connection);
|
|
2096
|
+
}
|
|
2097
|
+
return connectionHolder.connection;
|
|
2098
|
+
}
|
|
2099
|
+
function getDb() {
|
|
2100
|
+
getDatabase();
|
|
2101
|
+
return getDefaultDatabaseQuery();
|
|
2102
|
+
}
|
|
2103
|
+
var db = new Proxy(function database() {}, {
|
|
2104
|
+
apply(_target, _thisArg, args) {
|
|
2105
|
+
return getDb()(...args);
|
|
2106
|
+
},
|
|
2107
|
+
get(_target, property) {
|
|
2108
|
+
const connection = getDb();
|
|
2109
|
+
const value = connection[property];
|
|
2110
|
+
return typeof value === "function" ? value.bind(connection) : value;
|
|
2111
|
+
}
|
|
2112
|
+
});
|
|
2113
|
+
|
|
2561
2114
|
// ../../src/modules/user/apiTokenTable.ts
|
|
2562
2115
|
var apiTokenTable = defineTable({
|
|
2563
2116
|
name: "api_token",
|
|
@@ -2690,9 +2243,6 @@ function revealMfaSecret(stored) {
|
|
|
2690
2243
|
// ../../src/core/auth/authContext.ts
|
|
2691
2244
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
2692
2245
|
var authContext = new AsyncLocalStorage2;
|
|
2693
|
-
function runWithAuthUser(user, callback) {
|
|
2694
|
-
return authContext.run(user, callback);
|
|
2695
|
-
}
|
|
2696
2246
|
function currentAuthUser() {
|
|
2697
2247
|
return authContext.getStore() ?? null;
|
|
2698
2248
|
}
|
|
@@ -2700,9 +2250,6 @@ function currentAuthUser() {
|
|
|
2700
2250
|
// ../../src/core/http/requestMetaContext.ts
|
|
2701
2251
|
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
2702
2252
|
var requestMetaContext = new AsyncLocalStorage3;
|
|
2703
|
-
function runWithRequestMeta(meta, callback) {
|
|
2704
|
-
return requestMetaContext.run(meta, callback);
|
|
2705
|
-
}
|
|
2706
2253
|
function currentRequestMeta() {
|
|
2707
2254
|
return requestMetaContext.getStore() ?? {
|
|
2708
2255
|
ipAddress: null,
|
|
@@ -2788,25 +2335,12 @@ function verifyTotp(secret, token, window = 1) {
|
|
|
2788
2335
|
// ../../src/core/tenant/tenantContext.ts
|
|
2789
2336
|
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
2790
2337
|
var tenantContext = new AsyncLocalStorage4;
|
|
2791
|
-
function runWithTenant(tenant, callback) {
|
|
2792
|
-
return tenantContext.run(tenant, callback);
|
|
2793
|
-
}
|
|
2794
2338
|
function currentTenant() {
|
|
2795
2339
|
return tenantContext.getStore() ?? null;
|
|
2796
2340
|
}
|
|
2797
2341
|
function currentTenantId() {
|
|
2798
2342
|
return currentTenant()?.id ?? 1;
|
|
2799
2343
|
}
|
|
2800
|
-
function rateLimitMultiplierForPlan(plan) {
|
|
2801
|
-
switch (plan) {
|
|
2802
|
-
case "enterprise":
|
|
2803
|
-
return 4;
|
|
2804
|
-
case "pro":
|
|
2805
|
-
return 2;
|
|
2806
|
-
default:
|
|
2807
|
-
return 1;
|
|
2808
|
-
}
|
|
2809
|
-
}
|
|
2810
2344
|
|
|
2811
2345
|
// ../../src/domain/abilities.ts
|
|
2812
2346
|
var MEMBER_ABILITIES = [
|
|
@@ -3018,9 +2552,6 @@ function readRequestCookie(request, name) {
|
|
|
3018
2552
|
}
|
|
3019
2553
|
return null;
|
|
3020
2554
|
}
|
|
3021
|
-
function readBunRequestCookie(request, name) {
|
|
3022
|
-
return request.cookies.get(name) ?? readRequestCookie(request, name);
|
|
3023
|
-
}
|
|
3024
2555
|
|
|
3025
2556
|
// ../../src/core/http/csrfToken.ts
|
|
3026
2557
|
var CSRF_COOKIE = "workhub_csrf";
|