@getstrata/core 0.5.14 → 0.5.16
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/bootstrap/contracts.d.ts +2 -0
- package/dist/bootstrap/http/securedRouteModelBinding.d.ts +11 -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/http/securedRouteModelBinding.d.ts +2 -11
- 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/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/cache/createCacheStore.js +376 -0
- 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/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 +30 -6
- package/dist/index.js +2529 -2031
- package/dist/modules/user/repository.d.ts +1 -0
- package/package.json +11 -5
package/dist/entries/view.js
CHANGED
|
@@ -45,9 +45,6 @@ function isHtmxRequest(request) {
|
|
|
45
45
|
return request.headers.get("HX-Request") === "true";
|
|
46
46
|
}
|
|
47
47
|
// ../../src/bootstrap/config.ts
|
|
48
|
-
var CORE_QUEUE_TOKEN = "core.queue";
|
|
49
|
-
var CORE_POLICY_GATE_TOKEN = "core.policyGate";
|
|
50
|
-
var CORE_AUTH_TOKEN = "core.auth";
|
|
51
48
|
var CORE_TOKEN_SERVICE_TOKEN = "core.tokenService";
|
|
52
49
|
var DEFAULT_QUEUE_DRIVER = "sync";
|
|
53
50
|
|
|
@@ -479,14 +476,6 @@ function appendWhereParts(tableName, where, params) {
|
|
|
479
476
|
}
|
|
480
477
|
return clauses.join(" AND ");
|
|
481
478
|
}
|
|
482
|
-
function buildWhereClause(tableName, where = {}) {
|
|
483
|
-
const params = [];
|
|
484
|
-
const body = appendWhereParts(tableName, where, params);
|
|
485
|
-
return {
|
|
486
|
-
clause: body.length > 0 ? ` WHERE ${body}` : "",
|
|
487
|
-
params
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
479
|
function buildWhereNodeClause(tableName, node, params) {
|
|
491
480
|
if ("where" in node) {
|
|
492
481
|
return appendWhereParts(tableName, node.where, params);
|
|
@@ -789,30 +778,6 @@ function buildDeleteByIdQuery(table, id) {
|
|
|
789
778
|
}
|
|
790
779
|
|
|
791
780
|
// ../../src/core/database/relationships.ts
|
|
792
|
-
function hasMany(definition) {
|
|
793
|
-
return {
|
|
794
|
-
type: "hasMany",
|
|
795
|
-
...definition
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
function hasOne(definition) {
|
|
799
|
-
return {
|
|
800
|
-
type: "hasOne",
|
|
801
|
-
...definition
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
function belongsTo(definition) {
|
|
805
|
-
return {
|
|
806
|
-
type: "belongsTo",
|
|
807
|
-
...definition
|
|
808
|
-
};
|
|
809
|
-
}
|
|
810
|
-
function belongsToMany(definition) {
|
|
811
|
-
return {
|
|
812
|
-
type: "belongsToMany",
|
|
813
|
-
...definition
|
|
814
|
-
};
|
|
815
|
-
}
|
|
816
781
|
function indexHasManyRelation(parents, children, relation) {
|
|
817
782
|
const groups = new Map;
|
|
818
783
|
for (const parent of parents) {
|
|
@@ -828,15 +793,6 @@ function indexHasManyRelation(parents, children, relation) {
|
|
|
828
793
|
}
|
|
829
794
|
return groups;
|
|
830
795
|
}
|
|
831
|
-
function indexHasOneRelation(parents, children, relation) {
|
|
832
|
-
const grouped = indexHasManyRelation(parents, children, relation);
|
|
833
|
-
const result = new Map;
|
|
834
|
-
for (const parent of parents) {
|
|
835
|
-
const matches = grouped.get(parent[relation.localKey]) ?? [];
|
|
836
|
-
result.set(parent[relation.localKey], matches[0]);
|
|
837
|
-
}
|
|
838
|
-
return result;
|
|
839
|
-
}
|
|
840
796
|
function indexBelongsToRelation(children, parents, relation) {
|
|
841
797
|
const parentsById = new Map;
|
|
842
798
|
for (const parent of parents) {
|
|
@@ -852,45 +808,6 @@ function indexBelongsToRelation(children, parents, relation) {
|
|
|
852
808
|
}
|
|
853
809
|
return result;
|
|
854
810
|
}
|
|
855
|
-
function indexBelongsToManyRelation(parents, pivotRows, relatedRows, relation) {
|
|
856
|
-
const relatedById = new Map;
|
|
857
|
-
for (const related of relatedRows) {
|
|
858
|
-
relatedById.set(related[relation.relatedKey], related);
|
|
859
|
-
}
|
|
860
|
-
const groups = new Map;
|
|
861
|
-
for (const parent of parents) {
|
|
862
|
-
groups.set(parent[relation.parentKey], []);
|
|
863
|
-
}
|
|
864
|
-
for (const pivot of pivotRows) {
|
|
865
|
-
const parentId = pivot[relation.foreignPivotKey];
|
|
866
|
-
const relatedId = pivot[relation.relatedPivotKey];
|
|
867
|
-
const group = groups.get(parentId);
|
|
868
|
-
const related = relatedById.get(relatedId);
|
|
869
|
-
if (!group || !related) {
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
group.push(related);
|
|
873
|
-
}
|
|
874
|
-
return groups;
|
|
875
|
-
}
|
|
876
|
-
function morphMany(definition) {
|
|
877
|
-
return {
|
|
878
|
-
type: "morphMany",
|
|
879
|
-
...definition
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
function morphOne(definition) {
|
|
883
|
-
return {
|
|
884
|
-
type: "morphOne",
|
|
885
|
-
...definition
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
function morphTo(definition) {
|
|
889
|
-
return {
|
|
890
|
-
type: "morphTo",
|
|
891
|
-
...definition
|
|
892
|
-
};
|
|
893
|
-
}
|
|
894
811
|
function indexMorphManyRelation(parents, children, relation) {
|
|
895
812
|
const groups = new Map;
|
|
896
813
|
for (const parent of parents) {
|
|
@@ -909,15 +826,6 @@ function indexMorphManyRelation(parents, children, relation) {
|
|
|
909
826
|
}
|
|
910
827
|
return groups;
|
|
911
828
|
}
|
|
912
|
-
function indexMorphOneRelation(parents, children, relation) {
|
|
913
|
-
const grouped = indexMorphManyRelation(parents, children, relation);
|
|
914
|
-
const result = new Map;
|
|
915
|
-
for (const parent of parents) {
|
|
916
|
-
const matches = grouped.get(parent[relation.localKey]) ?? [];
|
|
917
|
-
result.set(parent[relation.localKey], matches[0]);
|
|
918
|
-
}
|
|
919
|
-
return result;
|
|
920
|
-
}
|
|
921
829
|
function indexMorphToRelation(children, parentsByType, relation) {
|
|
922
830
|
const result = new Map;
|
|
923
831
|
for (const child of children) {
|
|
@@ -934,92 +842,71 @@ function indexMorphToRelation(children, parentsByType, relation) {
|
|
|
934
842
|
return result;
|
|
935
843
|
}
|
|
936
844
|
|
|
937
|
-
// ../../src/
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
941
|
-
}
|
|
942
|
-
var databaseConfig = {
|
|
943
|
-
url: process.env.DATABASE_URL ?? "",
|
|
944
|
-
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
945
|
-
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
946
|
-
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
947
|
-
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
845
|
+
// ../../src/core/database/boundConnection.ts
|
|
846
|
+
var boundConnectionHolder = {
|
|
847
|
+
connection: null
|
|
948
848
|
};
|
|
849
|
+
function getBoundDatabaseConnection() {
|
|
850
|
+
return boundConnectionHolder.connection;
|
|
851
|
+
}
|
|
949
852
|
|
|
950
853
|
// ../../src/core/database/connectionContext.ts
|
|
951
854
|
import { AsyncLocalStorage } from "async_hooks";
|
|
952
855
|
var activeConnection = new AsyncLocalStorage;
|
|
953
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
954
|
-
return activeConnection.run(connection, callback);
|
|
955
|
-
}
|
|
956
856
|
function getActiveDatabaseConnection(fallback) {
|
|
957
857
|
return activeConnection.getStore() ?? fallback;
|
|
958
858
|
}
|
|
959
859
|
|
|
960
|
-
// ../../src/
|
|
961
|
-
var
|
|
962
|
-
function
|
|
963
|
-
|
|
964
|
-
|
|
860
|
+
// ../../src/core/database/queryProxy.ts
|
|
861
|
+
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
862
|
+
function createDatabaseQueryProxy(pool) {
|
|
863
|
+
function resolveDatabase() {
|
|
864
|
+
return getActiveDatabaseConnection(pool);
|
|
965
865
|
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
866
|
+
function resolveDatabaseForProperty(property) {
|
|
867
|
+
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
868
|
+
return pool;
|
|
869
|
+
}
|
|
870
|
+
return resolveDatabase();
|
|
871
|
+
}
|
|
872
|
+
return new Proxy(function database() {}, {
|
|
873
|
+
apply(_target, _thisArg, args) {
|
|
874
|
+
return resolveDatabase()(...args);
|
|
875
|
+
},
|
|
876
|
+
get(_target, property) {
|
|
877
|
+
const connection = resolveDatabaseForProperty(property);
|
|
878
|
+
const value = connection[property];
|
|
879
|
+
return typeof value === "function" ? value.bind(connection) : value;
|
|
880
|
+
}
|
|
972
881
|
});
|
|
973
882
|
}
|
|
974
883
|
|
|
975
|
-
// ../../src/
|
|
976
|
-
var
|
|
884
|
+
// ../../src/core/database/defaultConnection.ts
|
|
885
|
+
var defaultPool = {
|
|
977
886
|
connection: null
|
|
978
887
|
};
|
|
979
|
-
|
|
980
|
-
if (!connectionHolder.connection) {
|
|
981
|
-
connectionHolder.connection = createDatabaseConnection(databaseConfig);
|
|
982
|
-
}
|
|
983
|
-
return connectionHolder.connection;
|
|
984
|
-
}
|
|
985
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
986
|
-
function resolveDatabase() {
|
|
987
|
-
return getActiveDatabaseConnection(getDatabase());
|
|
988
|
-
}
|
|
989
|
-
function resolveDatabaseForProperty(property) {
|
|
990
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
991
|
-
return getDatabase();
|
|
992
|
-
}
|
|
993
|
-
return resolveDatabase();
|
|
994
|
-
}
|
|
995
|
-
var db = new Proxy(function database() {}, {
|
|
996
|
-
apply(_target, _thisArg, args) {
|
|
997
|
-
return resolveDatabase()(...args);
|
|
998
|
-
},
|
|
999
|
-
get(_target, property) {
|
|
1000
|
-
const connection = resolveDatabaseForProperty(property);
|
|
1001
|
-
const value = connection[property];
|
|
1002
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
1005
|
-
var connection_default = db;
|
|
1006
|
-
|
|
1007
|
-
// ../../src/core/database/boundConnection.ts
|
|
1008
|
-
var boundConnectionHolder = {
|
|
888
|
+
var defaultQuery = {
|
|
1009
889
|
connection: null
|
|
1010
890
|
};
|
|
1011
|
-
function
|
|
1012
|
-
|
|
891
|
+
function registerDefaultDatabasePool(connection) {
|
|
892
|
+
defaultPool.connection = connection;
|
|
893
|
+
defaultQuery.connection = createDatabaseQueryProxy(connection);
|
|
1013
894
|
}
|
|
1014
|
-
function
|
|
1015
|
-
|
|
895
|
+
function getDefaultDatabaseQuery() {
|
|
896
|
+
if (!defaultQuery.connection) {
|
|
897
|
+
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
898
|
+
}
|
|
899
|
+
return defaultQuery.connection;
|
|
1016
900
|
}
|
|
1017
901
|
|
|
1018
902
|
// ../../src/core/database/repositoryConnection.ts
|
|
1019
903
|
function resolveRepositoryConnection() {
|
|
1020
|
-
return getBoundDatabaseConnection() ??
|
|
904
|
+
return getBoundDatabaseConnection() ?? getDefaultDatabaseQuery();
|
|
1021
905
|
}
|
|
1022
|
-
var repositoryConnection = new Proxy({}, {
|
|
906
|
+
var repositoryConnection = new Proxy(function repositoryConnection2() {}, {
|
|
907
|
+
apply(_target, _thisArg, args) {
|
|
908
|
+
return resolveRepositoryConnection()(...args);
|
|
909
|
+
},
|
|
1023
910
|
get(_target, property) {
|
|
1024
911
|
const connection = resolveRepositoryConnection();
|
|
1025
912
|
const value = connection[property];
|
|
@@ -1582,327 +1469,10 @@ class BaseRepository {
|
|
|
1582
1469
|
}
|
|
1583
1470
|
}
|
|
1584
1471
|
var baseRepository_default = BaseRepository;
|
|
1585
|
-
// ../../src/core/database/connection.ts
|
|
1586
|
-
function createDatabaseConnection2(source) {
|
|
1587
|
-
return {
|
|
1588
|
-
async unsafe(query, params = []) {
|
|
1589
|
-
return await source.unsafe(query, params);
|
|
1590
|
-
}
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
1472
|
// ../../src/core/database/model.ts
|
|
1594
1473
|
var modelRepositories = new WeakMap;
|
|
1595
1474
|
var modelGlobalScopes = new WeakMap;
|
|
1596
1475
|
var modelBooted = new WeakSet;
|
|
1597
|
-
function resolveModelRepository(model) {
|
|
1598
|
-
const repository = modelRepositories.get(model);
|
|
1599
|
-
if (!repository) {
|
|
1600
|
-
throw new Error(`${model.name}.repository() is not implemented.`);
|
|
1601
|
-
}
|
|
1602
|
-
return repository;
|
|
1603
|
-
}
|
|
1604
|
-
function modelStatics(model) {
|
|
1605
|
-
return model;
|
|
1606
|
-
}
|
|
1607
|
-
function ensureBooted(model) {
|
|
1608
|
-
if (modelBooted.has(model)) {
|
|
1609
|
-
return;
|
|
1610
|
-
}
|
|
1611
|
-
modelBooted.add(model);
|
|
1612
|
-
const boot = model.boot;
|
|
1613
|
-
if (typeof boot === "function") {
|
|
1614
|
-
boot.call(model);
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
function getGlobalScopes(model) {
|
|
1618
|
-
return modelGlobalScopes.get(model) ?? [];
|
|
1619
|
-
}
|
|
1620
|
-
function hydrateValue(value, cast) {
|
|
1621
|
-
if (value === null || value === undefined) {
|
|
1622
|
-
return value;
|
|
1623
|
-
}
|
|
1624
|
-
switch (cast) {
|
|
1625
|
-
case "date":
|
|
1626
|
-
case "datetime":
|
|
1627
|
-
return value instanceof Date ? value : new Date(String(value));
|
|
1628
|
-
case "json":
|
|
1629
|
-
return typeof value === "string" ? JSON.parse(value) : value;
|
|
1630
|
-
case "bool":
|
|
1631
|
-
case "boolean":
|
|
1632
|
-
return value === true || value === 1 || value === "1" || value === "true";
|
|
1633
|
-
default:
|
|
1634
|
-
return value;
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
function dehydrateValue(value, cast) {
|
|
1638
|
-
if (value === null || value === undefined) {
|
|
1639
|
-
return value;
|
|
1640
|
-
}
|
|
1641
|
-
switch (cast) {
|
|
1642
|
-
case "date":
|
|
1643
|
-
case "datetime":
|
|
1644
|
-
return value instanceof Date ? value : new Date(String(value));
|
|
1645
|
-
case "json":
|
|
1646
|
-
return typeof value === "string" ? value : JSON.stringify(value);
|
|
1647
|
-
case "bool":
|
|
1648
|
-
case "boolean":
|
|
1649
|
-
return Boolean(value);
|
|
1650
|
-
default:
|
|
1651
|
-
return value;
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
function filterMassAssignable(fillable, guarded, input) {
|
|
1655
|
-
const resolvedGuarded = guarded ?? true;
|
|
1656
|
-
if (fillable && fillable.length > 0) {
|
|
1657
|
-
const allowed = new Set(fillable);
|
|
1658
|
-
return Object.fromEntries(Object.entries(input).filter(([key]) => allowed.has(key)));
|
|
1659
|
-
}
|
|
1660
|
-
if (resolvedGuarded === true || resolvedGuarded.includes("*")) {
|
|
1661
|
-
return {};
|
|
1662
|
-
}
|
|
1663
|
-
const blocked = new Set(resolvedGuarded);
|
|
1664
|
-
return Object.fromEntries(Object.entries(input).filter(([key]) => !blocked.has(key)));
|
|
1665
|
-
}
|
|
1666
|
-
function applyCasts(values, casts, direction) {
|
|
1667
|
-
if (Object.keys(casts).length === 0) {
|
|
1668
|
-
return values;
|
|
1669
|
-
}
|
|
1670
|
-
const result = { ...values };
|
|
1671
|
-
const castFn = direction === "hydrate" ? hydrateValue : dehydrateValue;
|
|
1672
|
-
for (const [key, cast] of Object.entries(casts)) {
|
|
1673
|
-
if (key in result && cast) {
|
|
1674
|
-
result[key] = castFn(result[key], cast);
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
-
return result;
|
|
1678
|
-
}
|
|
1679
|
-
function applyTimestampsOnCreate(columns, values, enabled) {
|
|
1680
|
-
if (!enabled) {
|
|
1681
|
-
return values;
|
|
1682
|
-
}
|
|
1683
|
-
const now = new Date;
|
|
1684
|
-
const result = { ...values };
|
|
1685
|
-
if (columns.includes("created_at")) {
|
|
1686
|
-
result.created_at = now;
|
|
1687
|
-
}
|
|
1688
|
-
if (columns.includes("updated_at")) {
|
|
1689
|
-
result.updated_at = now;
|
|
1690
|
-
}
|
|
1691
|
-
return result;
|
|
1692
|
-
}
|
|
1693
|
-
function applyTimestampsOnUpdate(columns, values, enabled) {
|
|
1694
|
-
if (!enabled) {
|
|
1695
|
-
return values;
|
|
1696
|
-
}
|
|
1697
|
-
const result = { ...values };
|
|
1698
|
-
if (columns.includes("updated_at")) {
|
|
1699
|
-
result.updated_at = new Date;
|
|
1700
|
-
}
|
|
1701
|
-
return result;
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
class Model {
|
|
1705
|
-
attributes;
|
|
1706
|
-
repository;
|
|
1707
|
-
static $fillable;
|
|
1708
|
-
static $guarded;
|
|
1709
|
-
static $casts = {};
|
|
1710
|
-
static $timestamps = true;
|
|
1711
|
-
_exists;
|
|
1712
|
-
constructor(attributes, repository, exists = true) {
|
|
1713
|
-
this.attributes = attributes;
|
|
1714
|
-
this.repository = repository;
|
|
1715
|
-
this._exists = exists;
|
|
1716
|
-
}
|
|
1717
|
-
get $exists() {
|
|
1718
|
-
return this._exists;
|
|
1719
|
-
}
|
|
1720
|
-
get(key) {
|
|
1721
|
-
return this.attributes[key];
|
|
1722
|
-
}
|
|
1723
|
-
get id() {
|
|
1724
|
-
return this.attributes[this.primaryKey()];
|
|
1725
|
-
}
|
|
1726
|
-
toObject() {
|
|
1727
|
-
return { ...this.attributes };
|
|
1728
|
-
}
|
|
1729
|
-
primaryKey() {
|
|
1730
|
-
throw new Error(`${this.constructor.name}.primaryKey() is not implemented.`);
|
|
1731
|
-
}
|
|
1732
|
-
static primaryKeyField() {
|
|
1733
|
-
return resolveModelRepository(this).getTable().primaryKey;
|
|
1734
|
-
}
|
|
1735
|
-
static hydrateAttributes(attributes) {
|
|
1736
|
-
const casts = modelStatics(this).$casts ?? {};
|
|
1737
|
-
return applyCasts(attributes, casts, "hydrate");
|
|
1738
|
-
}
|
|
1739
|
-
static dehydrateAttributes(attributes) {
|
|
1740
|
-
const casts = modelStatics(this).$casts ?? {};
|
|
1741
|
-
return applyCasts(attributes, casts, "dehydrate");
|
|
1742
|
-
}
|
|
1743
|
-
static fromRecord(record, repository, exists = true) {
|
|
1744
|
-
const statics = modelStatics(this);
|
|
1745
|
-
const hydrated = statics.hydrateAttributes(record);
|
|
1746
|
-
return new statics(hydrated, repository, exists);
|
|
1747
|
-
}
|
|
1748
|
-
static boot() {}
|
|
1749
|
-
static addGlobalScope(_name, scope) {
|
|
1750
|
-
ensureBooted(this);
|
|
1751
|
-
const existing = modelGlobalScopes.get(this) ?? [];
|
|
1752
|
-
modelGlobalScopes.set(this, [
|
|
1753
|
-
...existing,
|
|
1754
|
-
scope
|
|
1755
|
-
]);
|
|
1756
|
-
}
|
|
1757
|
-
static repository() {
|
|
1758
|
-
return resolveModelRepository(this);
|
|
1759
|
-
}
|
|
1760
|
-
static query() {
|
|
1761
|
-
ensureBooted(this);
|
|
1762
|
-
const repository = resolveModelRepository(this);
|
|
1763
|
-
let query = repository.query();
|
|
1764
|
-
for (const scope of getGlobalScopes(this)) {
|
|
1765
|
-
query = scope(query);
|
|
1766
|
-
}
|
|
1767
|
-
return query;
|
|
1768
|
-
}
|
|
1769
|
-
static async create(attributes) {
|
|
1770
|
-
const statics = modelStatics(this);
|
|
1771
|
-
ensureBooted(this);
|
|
1772
|
-
const repository = resolveModelRepository(this);
|
|
1773
|
-
const table = repository.getTable();
|
|
1774
|
-
const timestamps = statics.$timestamps ?? true;
|
|
1775
|
-
const assignable = filterMassAssignable(statics.$fillable, statics.$guarded, attributes);
|
|
1776
|
-
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
1777
|
-
const payload = statics.dehydrateAttributes(withTimestamps);
|
|
1778
|
-
const record = await repository.create(payload);
|
|
1779
|
-
return statics.fromRecord(record, repository, true);
|
|
1780
|
-
}
|
|
1781
|
-
static async find(id) {
|
|
1782
|
-
const statics = modelStatics(this);
|
|
1783
|
-
const repository = resolveModelRepository(this);
|
|
1784
|
-
const primaryKey = repository.getTable().primaryKey;
|
|
1785
|
-
const record = await Model.query.call(this).where({ [primaryKey]: id }).first();
|
|
1786
|
-
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1787
|
-
}
|
|
1788
|
-
static async findOrFail(id, errorFactory) {
|
|
1789
|
-
const model = await Model.find.call(this, id);
|
|
1790
|
-
if (model) {
|
|
1791
|
-
return model;
|
|
1792
|
-
}
|
|
1793
|
-
throw errorFactory?.(id) ?? new NotFoundError(`${this.name} ${String(id)} not found.`);
|
|
1794
|
-
}
|
|
1795
|
-
static async all(options = {}) {
|
|
1796
|
-
const statics = modelStatics(this);
|
|
1797
|
-
const repository = resolveModelRepository(this);
|
|
1798
|
-
let query = Model.query.call(this);
|
|
1799
|
-
if (options.orderBy) {
|
|
1800
|
-
query = query.orderBy(options.orderBy);
|
|
1801
|
-
}
|
|
1802
|
-
if (options.limit !== undefined) {
|
|
1803
|
-
query = query.limit(options.limit);
|
|
1804
|
-
}
|
|
1805
|
-
const rows = await query.get();
|
|
1806
|
-
return rows.map((row) => statics.fromRecord(row, repository, true));
|
|
1807
|
-
}
|
|
1808
|
-
static async firstWhere(where, options = {}) {
|
|
1809
|
-
const statics = modelStatics(this);
|
|
1810
|
-
const repository = resolveModelRepository(this);
|
|
1811
|
-
let query = Model.query.call(this).where(where);
|
|
1812
|
-
if (options.orderBy) {
|
|
1813
|
-
query = query.orderBy(options.orderBy);
|
|
1814
|
-
}
|
|
1815
|
-
const record = await query.first();
|
|
1816
|
-
return record ? statics.fromRecord(record, repository, true) : null;
|
|
1817
|
-
}
|
|
1818
|
-
async save() {
|
|
1819
|
-
const ModelClass = modelStatics(this.constructor);
|
|
1820
|
-
const timestamps = ModelClass.$timestamps ?? true;
|
|
1821
|
-
const casts = ModelClass.$casts ?? {};
|
|
1822
|
-
const table = this.repository.getTable();
|
|
1823
|
-
if (this.$exists) {
|
|
1824
|
-
const changes = applyTimestampsOnUpdate(table.columns, applyCasts(this.attributes, casts, "dehydrate"), timestamps);
|
|
1825
|
-
const record2 = await this.repository.updateByIdOrThrow(this.id, changes);
|
|
1826
|
-
this.attributes = ModelClass.hydrateAttributes(record2);
|
|
1827
|
-
return this;
|
|
1828
|
-
}
|
|
1829
|
-
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
|
|
1830
|
-
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
1831
|
-
const payload = ModelClass.dehydrateAttributes(withTimestamps);
|
|
1832
|
-
const record = await this.repository.create(payload);
|
|
1833
|
-
this.attributes = ModelClass.hydrateAttributes(record);
|
|
1834
|
-
this._exists = true;
|
|
1835
|
-
return this;
|
|
1836
|
-
}
|
|
1837
|
-
async update(changes) {
|
|
1838
|
-
const ModelClass = modelStatics(this.constructor);
|
|
1839
|
-
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, changes);
|
|
1840
|
-
Object.assign(this.attributes, assignable);
|
|
1841
|
-
return await this.save();
|
|
1842
|
-
}
|
|
1843
|
-
async delete() {
|
|
1844
|
-
if (resolveSoftDeleteColumn(this.repository.getTable())) {
|
|
1845
|
-
return await this.repository.deleteById(this.id);
|
|
1846
|
-
}
|
|
1847
|
-
return await this.repository.forceDeleteById(this.id);
|
|
1848
|
-
}
|
|
1849
|
-
async forceDelete() {
|
|
1850
|
-
return await this.repository.forceDeleteById(this.id);
|
|
1851
|
-
}
|
|
1852
|
-
async restore() {
|
|
1853
|
-
const ModelClass = modelStatics(this.constructor);
|
|
1854
|
-
const record = await this.repository.restoreById(this.id);
|
|
1855
|
-
if (!record) {
|
|
1856
|
-
return null;
|
|
1857
|
-
}
|
|
1858
|
-
this.attributes = ModelClass.hydrateAttributes(record);
|
|
1859
|
-
return this;
|
|
1860
|
-
}
|
|
1861
|
-
async loadHasMany(as, relation, childRepository, options = {}) {
|
|
1862
|
-
const grouped = await childRepository.withConnection(this.repository.getConnection()).loadHasManyForParents([this.attributes], relation, options);
|
|
1863
|
-
const loaded = grouped.get(this.attributes[relation.localKey]) ?? [];
|
|
1864
|
-
return Object.assign(this, { [as]: loaded });
|
|
1865
|
-
}
|
|
1866
|
-
async loadHasOne(as, relation, childRepository, options = {}) {
|
|
1867
|
-
const loaded = await this.loadHasMany(as, relation, childRepository, { ...options, limit: 1 });
|
|
1868
|
-
const value = loaded[as]?.[0];
|
|
1869
|
-
return Object.assign(this, { [as]: value });
|
|
1870
|
-
}
|
|
1871
|
-
async loadBelongsTo(as, relation, parentRepository, options = {}) {
|
|
1872
|
-
const grouped = await this.repository.loadBelongsToForParents([this.attributes], relation, parentRepository, options);
|
|
1873
|
-
const loaded = grouped.get(this.attributes[relation.foreignKey]);
|
|
1874
|
-
return Object.assign(this, { [as]: loaded });
|
|
1875
|
-
}
|
|
1876
|
-
async loadBelongsToMany(as, relation, relatedRepository, options = {}) {
|
|
1877
|
-
const connection = this.repository.getConnection();
|
|
1878
|
-
const parentId = this.attributes[relation.parentKey];
|
|
1879
|
-
const pivotRows = await connection.unsafe(`SELECT * FROM ${relation.pivotTable} WHERE ${String(relation.foreignPivotKey)} = $1`, [parentId]);
|
|
1880
|
-
if (pivotRows.length === 0) {
|
|
1881
|
-
return Object.assign(this, { [as]: [] });
|
|
1882
|
-
}
|
|
1883
|
-
const relatedIds = [
|
|
1884
|
-
...new Set(pivotRows.map((row) => row[relation.relatedPivotKey]))
|
|
1885
|
-
];
|
|
1886
|
-
const relatedRows = await relatedRepository.withConnection(connection).findAll({
|
|
1887
|
-
...options,
|
|
1888
|
-
where: {
|
|
1889
|
-
[relation.relatedKey]: relatedIds
|
|
1890
|
-
}
|
|
1891
|
-
});
|
|
1892
|
-
const grouped = indexBelongsToManyRelation([this.attributes], pivotRows, relatedRows, relation);
|
|
1893
|
-
const loaded = grouped.get(parentId) ?? [];
|
|
1894
|
-
return Object.assign(this, { [as]: loaded });
|
|
1895
|
-
}
|
|
1896
|
-
mergeAttributes(patch) {
|
|
1897
|
-
Object.assign(this.attributes, patch);
|
|
1898
|
-
return this;
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
function registerModelRepository(model, repository) {
|
|
1902
|
-
modelRepositories.set(model, repository);
|
|
1903
|
-
ensureBooted(model);
|
|
1904
|
-
return model;
|
|
1905
|
-
}
|
|
1906
1476
|
// ../../src/core/database/schema/columnDefinition.ts
|
|
1907
1477
|
class ColumnDefinition {
|
|
1908
1478
|
name;
|
|
@@ -2124,45 +1694,6 @@ class Blueprint {
|
|
|
2124
1694
|
});
|
|
2125
1695
|
}
|
|
2126
1696
|
}
|
|
2127
|
-
// ../../src/core/database/schema/driver.ts
|
|
2128
|
-
function normalizeConnectionName(connection) {
|
|
2129
|
-
const normalized = connection.trim().toLowerCase();
|
|
2130
|
-
if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
|
|
2131
|
-
return "pgsql";
|
|
2132
|
-
}
|
|
2133
|
-
if (normalized === "mysql" || normalized === "mariadb") {
|
|
2134
|
-
return "mysql";
|
|
2135
|
-
}
|
|
2136
|
-
if (normalized === "sqlite") {
|
|
2137
|
-
return "sqlite";
|
|
2138
|
-
}
|
|
2139
|
-
throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
|
|
2140
|
-
}
|
|
2141
|
-
function resolveDriverFromUrl(url) {
|
|
2142
|
-
const normalized = url.trim().toLowerCase();
|
|
2143
|
-
if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
|
|
2144
|
-
return "pgsql";
|
|
2145
|
-
}
|
|
2146
|
-
if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
|
|
2147
|
-
return "mysql";
|
|
2148
|
-
}
|
|
2149
|
-
if (normalized.startsWith("sqlite:")) {
|
|
2150
|
-
return "sqlite";
|
|
2151
|
-
}
|
|
2152
|
-
return null;
|
|
2153
|
-
}
|
|
2154
|
-
function resolveDatabaseDriver(options = {}) {
|
|
2155
|
-
const connection = options.connection ?? process.env.DB_CONNECTION;
|
|
2156
|
-
if (connection) {
|
|
2157
|
-
return normalizeConnectionName(connection);
|
|
2158
|
-
}
|
|
2159
|
-
const url = options.url ?? process.env.DATABASE_URL ?? "";
|
|
2160
|
-
const fromUrl = resolveDriverFromUrl(url);
|
|
2161
|
-
if (fromUrl) {
|
|
2162
|
-
return fromUrl;
|
|
2163
|
-
}
|
|
2164
|
-
return "pgsql";
|
|
2165
|
-
}
|
|
2166
1697
|
// ../../src/core/database/schema/errors.ts
|
|
2167
1698
|
class UnsupportedSchemaFeatureError extends Error {
|
|
2168
1699
|
constructor(feature, driver) {
|
|
@@ -2501,48 +2032,70 @@ class SchemaBuilder {
|
|
|
2501
2032
|
toSql() {
|
|
2502
2033
|
return [...this.#statements];
|
|
2503
2034
|
}
|
|
2504
|
-
async execute(
|
|
2035
|
+
async execute(db) {
|
|
2505
2036
|
for (const statement of this.#statements) {
|
|
2506
|
-
await
|
|
2037
|
+
await db.unsafe(statement);
|
|
2507
2038
|
}
|
|
2508
2039
|
}
|
|
2509
2040
|
}
|
|
2510
|
-
|
|
2511
|
-
class Schema {
|
|
2512
|
-
static builder(driver) {
|
|
2513
|
-
return new SchemaBuilder(driver ?? resolveDatabaseDriver());
|
|
2514
|
-
}
|
|
2515
|
-
static async run(db2, driver, callback) {
|
|
2516
|
-
const schema = Schema.builder(driver);
|
|
2517
|
-
await callback(schema);
|
|
2518
|
-
await schema.execute(db2);
|
|
2519
|
-
}
|
|
2520
|
-
}
|
|
2521
|
-
function createSchemaBuilder(db2, driver) {
|
|
2522
|
-
const builder = Schema.builder(driver);
|
|
2523
|
-
return Object.assign(builder, {
|
|
2524
|
-
async commit() {
|
|
2525
|
-
await builder.execute(db2);
|
|
2526
|
-
}
|
|
2527
|
-
});
|
|
2528
|
-
}
|
|
2529
2041
|
// ../../src/core/database/table.ts
|
|
2530
2042
|
function defineTable(definition) {
|
|
2531
2043
|
return definition;
|
|
2532
2044
|
}
|
|
2533
|
-
// ../../src/
|
|
2534
|
-
function
|
|
2535
|
-
|
|
2045
|
+
// ../../src/config/database.ts
|
|
2046
|
+
function readInteger(name, fallback) {
|
|
2047
|
+
const parsed = Number.parseInt(process.env[name] ?? String(fallback), 10);
|
|
2048
|
+
return Number.isInteger(parsed) && parsed >= 0 ? parsed : fallback;
|
|
2536
2049
|
}
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2050
|
+
var databaseConfig = {
|
|
2051
|
+
url: process.env.DATABASE_URL ?? "",
|
|
2052
|
+
poolMax: readInteger("DB_POOL_MAX", 10),
|
|
2053
|
+
idleTimeoutSeconds: readInteger("DB_POOL_IDLE_TIMEOUT", 30),
|
|
2054
|
+
maxLifetimeSeconds: readInteger("DB_POOL_MAX_LIFETIME", 3600),
|
|
2055
|
+
connectionTimeoutSeconds: readInteger("DB_CONNECTION_TIMEOUT", 10)
|
|
2056
|
+
};
|
|
2057
|
+
|
|
2058
|
+
// ../../src/db/connection/createConnection.ts
|
|
2059
|
+
var {SQL } = globalThis.Bun;
|
|
2060
|
+
function createDatabaseConnection2(config) {
|
|
2061
|
+
if (!config.url) {
|
|
2062
|
+
throw new Error("DATABASE_URL is not configured. Set DATABASE_URL before starting the app or running integration tests.");
|
|
2541
2063
|
}
|
|
2542
|
-
return
|
|
2543
|
-
|
|
2064
|
+
return new SQL({
|
|
2065
|
+
url: config.url,
|
|
2066
|
+
max: config.poolMax,
|
|
2067
|
+
idleTimeout: config.idleTimeoutSeconds,
|
|
2068
|
+
maxLifetime: config.maxLifetimeSeconds,
|
|
2069
|
+
connectionTimeout: config.connectionTimeoutSeconds
|
|
2544
2070
|
});
|
|
2545
2071
|
}
|
|
2072
|
+
|
|
2073
|
+
// ../../src/db/connection/index.ts
|
|
2074
|
+
var connectionHolder = {
|
|
2075
|
+
connection: null
|
|
2076
|
+
};
|
|
2077
|
+
function getDatabase() {
|
|
2078
|
+
if (!connectionHolder.connection) {
|
|
2079
|
+
connectionHolder.connection = createDatabaseConnection2(databaseConfig);
|
|
2080
|
+
registerDefaultDatabasePool(connectionHolder.connection);
|
|
2081
|
+
}
|
|
2082
|
+
return connectionHolder.connection;
|
|
2083
|
+
}
|
|
2084
|
+
function getDb() {
|
|
2085
|
+
getDatabase();
|
|
2086
|
+
return getDefaultDatabaseQuery();
|
|
2087
|
+
}
|
|
2088
|
+
var db = new Proxy(function database() {}, {
|
|
2089
|
+
apply(_target, _thisArg, args) {
|
|
2090
|
+
return getDb()(...args);
|
|
2091
|
+
},
|
|
2092
|
+
get(_target, property) {
|
|
2093
|
+
const connection = getDb();
|
|
2094
|
+
const value = connection[property];
|
|
2095
|
+
return typeof value === "function" ? value.bind(connection) : value;
|
|
2096
|
+
}
|
|
2097
|
+
});
|
|
2098
|
+
|
|
2546
2099
|
// ../../src/modules/user/apiTokenTable.ts
|
|
2547
2100
|
var apiTokenTable = defineTable({
|
|
2548
2101
|
name: "api_token",
|
|
@@ -2675,9 +2228,6 @@ function revealMfaSecret(stored) {
|
|
|
2675
2228
|
// ../../src/core/auth/authContext.ts
|
|
2676
2229
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
2677
2230
|
var authContext = new AsyncLocalStorage2;
|
|
2678
|
-
function runWithAuthUser(user, callback) {
|
|
2679
|
-
return authContext.run(user, callback);
|
|
2680
|
-
}
|
|
2681
2231
|
function currentAuthUser() {
|
|
2682
2232
|
return authContext.getStore() ?? null;
|
|
2683
2233
|
}
|
|
@@ -2685,9 +2235,6 @@ function currentAuthUser() {
|
|
|
2685
2235
|
// ../../src/core/http/requestMetaContext.ts
|
|
2686
2236
|
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
2687
2237
|
var requestMetaContext = new AsyncLocalStorage3;
|
|
2688
|
-
function runWithRequestMeta(meta, callback) {
|
|
2689
|
-
return requestMetaContext.run(meta, callback);
|
|
2690
|
-
}
|
|
2691
2238
|
function currentRequestMeta() {
|
|
2692
2239
|
return requestMetaContext.getStore() ?? {
|
|
2693
2240
|
ipAddress: null,
|
|
@@ -2773,25 +2320,12 @@ function verifyTotp(secret, token, window = 1) {
|
|
|
2773
2320
|
// ../../src/core/tenant/tenantContext.ts
|
|
2774
2321
|
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
2775
2322
|
var tenantContext = new AsyncLocalStorage4;
|
|
2776
|
-
function runWithTenant(tenant, callback) {
|
|
2777
|
-
return tenantContext.run(tenant, callback);
|
|
2778
|
-
}
|
|
2779
2323
|
function currentTenant() {
|
|
2780
2324
|
return tenantContext.getStore() ?? null;
|
|
2781
2325
|
}
|
|
2782
2326
|
function currentTenantId() {
|
|
2783
2327
|
return currentTenant()?.id ?? 1;
|
|
2784
2328
|
}
|
|
2785
|
-
function rateLimitMultiplierForPlan(plan) {
|
|
2786
|
-
switch (plan) {
|
|
2787
|
-
case "enterprise":
|
|
2788
|
-
return 4;
|
|
2789
|
-
case "pro":
|
|
2790
|
-
return 2;
|
|
2791
|
-
default:
|
|
2792
|
-
return 1;
|
|
2793
|
-
}
|
|
2794
|
-
}
|
|
2795
2329
|
|
|
2796
2330
|
// ../../src/domain/abilities.ts
|
|
2797
2331
|
var MEMBER_ABILITIES = [
|
|
@@ -3003,9 +2537,6 @@ function readRequestCookie(request, name) {
|
|
|
3003
2537
|
}
|
|
3004
2538
|
return null;
|
|
3005
2539
|
}
|
|
3006
|
-
function readBunRequestCookie(request, name) {
|
|
3007
|
-
return request.cookies.get(name) ?? readRequestCookie(request, name);
|
|
3008
|
-
}
|
|
3009
2540
|
|
|
3010
2541
|
// ../../src/core/http/csrfToken.ts
|
|
3011
2542
|
var CSRF_COOKIE = "workhub_csrf";
|