@getstrata/core 2.0.8 → 2.1.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/CHANGELOG.md +8 -0
- package/dist/core/contracts/container.d.ts +28 -10
- package/dist/core/contracts/di.d.ts +3 -2
- package/dist/core/database/model.d.ts +83 -31
- package/dist/core/http/middleware.d.ts +2 -2
- package/dist/core/http/response.d.ts +2 -0
- package/dist/core/http/routeMiddleware.d.ts +3 -2
- package/dist/entries/contracts/container.js +8 -1
- package/dist/entries/database/model.js +35 -14
- package/dist/entries/http/routeMiddleware.js +1 -4
- package/dist/framework/public-api.d.ts +1 -1
- package/dist/index.js +38 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
- refactor(starter): reuse shared config and base repositories - fix(database): await password casts before model writes - fix(database): preserve concrete model types in query results
|
|
6
|
+
|
|
7
|
+
## 2.0.9
|
|
8
|
+
|
|
9
|
+
- fix(starter): remove demo credentials from public login defaults
|
|
10
|
+
|
|
3
11
|
## 2.0.8
|
|
4
12
|
|
|
5
13
|
- fix(schema): preserve constraints and generate builder migrations
|
|
@@ -1,19 +1,37 @@
|
|
|
1
|
+
declare const serviceTokenType: unique symbol;
|
|
2
|
+
/** Invariant service identity; its runtime value remains the existing string key. */
|
|
3
|
+
type ServiceToken<T> = string & {
|
|
4
|
+
readonly [serviceTokenType]: (value: T) => T;
|
|
5
|
+
};
|
|
6
|
+
/** Prevent a typed token from falling back to an unchecked legacy overload. */
|
|
7
|
+
type LegacyServiceKey = string & {
|
|
8
|
+
readonly [serviceTokenType]?: never;
|
|
9
|
+
};
|
|
10
|
+
declare function createServiceToken<T>(key: string): ServiceToken<T>;
|
|
1
11
|
type ServiceFactory<T> = (container: ServiceContainer) => T;
|
|
2
12
|
interface ServiceContainerLike {
|
|
3
13
|
has(key: string): boolean;
|
|
4
|
-
resolve<T>(key:
|
|
14
|
+
resolve<T>(key: ServiceToken<T>): T;
|
|
15
|
+
resolve<T>(key: LegacyServiceKey): T;
|
|
5
16
|
}
|
|
6
17
|
declare class ServiceContainer implements ServiceContainerLike {
|
|
7
18
|
private readonly services;
|
|
8
19
|
private readonly singletonFactories;
|
|
9
20
|
private readonly bindings;
|
|
10
|
-
set<T>(key:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
set<T>(key: ServiceToken<T>, value: NoInfer<T>): T;
|
|
22
|
+
set<T>(key: LegacyServiceKey, value: T): T;
|
|
23
|
+
singleton<T>(key: ServiceToken<T>, factory: ServiceFactory<NoInfer<T>>): void;
|
|
24
|
+
singleton<T>(key: LegacyServiceKey, factory: ServiceFactory<T>): void;
|
|
25
|
+
bind<T>(key: ServiceToken<T>, factory: ServiceFactory<NoInfer<T>>): void;
|
|
26
|
+
bind<T>(key: LegacyServiceKey, factory: ServiceFactory<T>): void;
|
|
27
|
+
get<T>(key: ServiceToken<T>): T;
|
|
28
|
+
get<T>(key: LegacyServiceKey): T;
|
|
29
|
+
resolve<T>(key: ServiceToken<T>): T;
|
|
30
|
+
resolve<T>(key: LegacyServiceKey): T;
|
|
31
|
+
make<T>(key: ServiceToken<T>): T;
|
|
32
|
+
make<T>(key: LegacyServiceKey): T;
|
|
33
|
+
instance<T>(key: ServiceToken<T>, value: NoInfer<T>): T;
|
|
34
|
+
instance<T>(key: LegacyServiceKey, value: T): T;
|
|
17
35
|
has(key: string): boolean;
|
|
18
36
|
}
|
|
19
37
|
interface ConfigStoreLike {
|
|
@@ -28,5 +46,5 @@ declare class ConfigStore implements ConfigStoreLike {
|
|
|
28
46
|
require<T>(key: string): T;
|
|
29
47
|
has(key: string): boolean;
|
|
30
48
|
}
|
|
31
|
-
export type { ConfigStoreLike, ServiceContainerLike, ServiceFactory };
|
|
32
|
-
export { ConfigStore, ServiceContainer };
|
|
49
|
+
export type { ConfigStoreLike, LegacyServiceKey, ServiceContainerLike, ServiceFactory, ServiceToken, };
|
|
50
|
+
export { ConfigStore, createServiceToken, ServiceContainer };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CacheLike } from "../../types/services";
|
|
2
2
|
import type { StorageManager } from "../storage/storage";
|
|
3
|
-
import type { ConfigStore, ConfigStoreLike, ServiceContainer, ServiceFactory } from "./container";
|
|
3
|
+
import type { ConfigStore, ConfigStoreLike, LegacyServiceKey, ServiceContainer, ServiceFactory, ServiceToken } from "./container";
|
|
4
4
|
type AppRouteMap = Record<string, any>;
|
|
5
5
|
type CachedJson = <T>(cacheKey: string, loader: () => Promise<T>, tags?: string[], request?: Request) => Promise<Response>;
|
|
6
6
|
interface AppDependencies {
|
|
@@ -26,6 +26,7 @@ interface AppContext {
|
|
|
26
26
|
}
|
|
27
27
|
declare function getRequiredDependency<K extends keyof AppDependencies>(dependencies: Partial<AppDependencies>, key: K): AppDependencies[K];
|
|
28
28
|
declare function assertAppDependenciesComplete(dependencies: MutableAppDependencies): asserts dependencies is AppDependencies;
|
|
29
|
-
declare function resolveService<T>(dependencies: AppDependencies, token:
|
|
29
|
+
declare function resolveService<T>(dependencies: AppDependencies, token: ServiceToken<T>): T;
|
|
30
|
+
declare function resolveService<T>(dependencies: AppDependencies, token: LegacyServiceKey): T;
|
|
30
31
|
export type { AppContext, AppDependencies, AppRouteMap, CachedJson, MutableAppDependencies, ProviderContext, ServiceFactory, ServiceProvider, };
|
|
31
32
|
export { assertAppDependenciesComplete, getRequiredDependency, resolveService };
|
|
@@ -13,6 +13,14 @@ interface ModelConstructor<TEntity extends object, PrimaryKey extends keyof TEnt
|
|
|
13
13
|
new (attributes: TEntity, repository: BaseRepository<TEntity, PrimaryKey>, exists?: boolean): TModel;
|
|
14
14
|
}
|
|
15
15
|
type AnyModel = Model<Record<string, unknown>, "id">;
|
|
16
|
+
type ModelAttributes<TModel> = TModel extends {
|
|
17
|
+
toObject(): infer TRecord extends object;
|
|
18
|
+
} ? TRecord : Record<string, unknown>;
|
|
19
|
+
type WithModelCount<TModel, TCount extends string> = {
|
|
20
|
+
toObject(): Omit<ModelAttributes<TModel>, TCount> & Record<TCount, unknown>;
|
|
21
|
+
get<K extends keyof (Omit<ModelAttributes<TModel>, TCount> & Record<TCount, unknown>)>(key: K): (Omit<ModelAttributes<TModel>, TCount> & Record<TCount, unknown>)[K];
|
|
22
|
+
} & TModel;
|
|
23
|
+
type ModelQueryResult<TModel, TCounts extends string> = [TCounts] extends [never] ? TModel : WithModelCount<TModel, TCounts>;
|
|
16
24
|
type RelatedRef<TRelated extends object, RelatedKey extends keyof TRelated & string> = RelatedModelClass<TRelated, RelatedKey> | string | (() => RelatedModelClass<TRelated, RelatedKey>);
|
|
17
25
|
type RelationNameInput = string | readonly string[];
|
|
18
26
|
type ModelObserver = {
|
|
@@ -29,10 +37,14 @@ type ModelObserver = {
|
|
|
29
37
|
/** Alias for `hasMany("Name")` when `Name` is not `constructor.name` or `$morphClass`. */
|
|
30
38
|
declare function registerModelClass(name: string, model: object): void;
|
|
31
39
|
declare function hydrateValue(value: unknown, cast: CastType): unknown;
|
|
40
|
+
declare function dehydrateValue(value: unknown, cast: "hashed"): Promise<unknown>;
|
|
41
|
+
declare function dehydrateValue(value: unknown, cast: Exclude<CastType, "hashed">): unknown;
|
|
32
42
|
declare function dehydrateValue(value: unknown, cast: CastType): unknown;
|
|
33
43
|
declare function filterMassAssignable(fillable: readonly string[] | undefined, guarded: readonly string[] | true | undefined, input: LoadedAttributes): LoadedAttributes;
|
|
34
|
-
declare function applyCasts(values: LoadedAttributes, casts: ModelCasts, direction: "hydrate"
|
|
35
|
-
declare
|
|
44
|
+
declare function applyCasts(values: LoadedAttributes, casts: ModelCasts, direction: "hydrate"): LoadedAttributes;
|
|
45
|
+
declare function applyCasts(values: LoadedAttributes, casts: ModelCasts, direction: "dehydrate"): Promise<LoadedAttributes>;
|
|
46
|
+
declare function applyCasts(values: LoadedAttributes, casts: ModelCasts, direction: "hydrate" | "dehydrate"): LoadedAttributes | Promise<LoadedAttributes>;
|
|
47
|
+
declare class ModelQuery<TModel extends object = AnyModel, TCounts extends string = never> {
|
|
36
48
|
private readonly modelClass;
|
|
37
49
|
readonly query: RepositoryQuery<Record<string, unknown>, "id">;
|
|
38
50
|
private readonly eager;
|
|
@@ -55,7 +67,7 @@ declare class ModelQuery {
|
|
|
55
67
|
page: number;
|
|
56
68
|
perPage: number;
|
|
57
69
|
}): Promise<{
|
|
58
|
-
data:
|
|
70
|
+
data: ModelQueryResult<TModel, TCounts>[];
|
|
59
71
|
meta: Awaited<ReturnType<RepositoryQuery<Record<string, unknown>, "id">["paginate"]>>["meta"];
|
|
60
72
|
}>;
|
|
61
73
|
whereExists(sql: string, params?: readonly unknown[]): this;
|
|
@@ -73,17 +85,17 @@ declare class ModelQuery {
|
|
|
73
85
|
withHasManyThrough(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withHasManyThrough"]>): this;
|
|
74
86
|
withTrashed(): this;
|
|
75
87
|
onlyTrashed(): this;
|
|
76
|
-
get(): Promise<
|
|
88
|
+
get(): Promise<ModelQueryResult<TModel, TCounts>[]>;
|
|
77
89
|
private hydrateRows;
|
|
78
|
-
first(): Promise<
|
|
90
|
+
first(): Promise<ModelQueryResult<TModel, TCounts> | null>;
|
|
79
91
|
count(): Promise<number>;
|
|
80
92
|
pluck(column: string): Promise<unknown[]>;
|
|
81
93
|
pluck(column: string, keyBy: string): Promise<Map<unknown, unknown>>;
|
|
82
94
|
value(column: string): Promise<unknown>;
|
|
83
|
-
find(id: unknown): Promise<
|
|
84
|
-
findOrFail(id: unknown, errorFactory?: (id: unknown) => Error): Promise<
|
|
85
|
-
then(onfulfilled?: ((value:
|
|
86
|
-
withCount(name:
|
|
95
|
+
find(id: unknown): Promise<ModelQueryResult<TModel, TCounts> | null>;
|
|
96
|
+
findOrFail(id: unknown, errorFactory?: (id: unknown) => Error): Promise<ModelQueryResult<TModel, TCounts>>;
|
|
97
|
+
then(onfulfilled?: ((value: ModelQueryResult<TModel, TCounts>[]) => unknown) | null, onrejected?: ((reason: unknown) => unknown) | null): Promise<unknown>;
|
|
98
|
+
withCount<TName extends string, TAlias extends string = `${TName}_count`>(name: TName, alias?: TAlias): ModelQuery<TModel, TCounts | TAlias>;
|
|
87
99
|
private constrainExists;
|
|
88
100
|
}
|
|
89
101
|
declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & string> {
|
|
@@ -116,24 +128,40 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
|
|
|
116
128
|
protected primaryKey(): PrimaryKey;
|
|
117
129
|
protected static primaryKeyField(this: object): string;
|
|
118
130
|
protected static hydrateAttributes<TEntity extends object>(this: object, attributes: TEntity): TEntity;
|
|
119
|
-
protected static dehydrateAttributes(this: object, attributes: LoadedAttributes): LoadedAttributes
|
|
131
|
+
protected static dehydrateAttributes(this: object, attributes: LoadedAttributes): LoadedAttributes | Promise<LoadedAttributes>;
|
|
120
132
|
protected static fromRecord<TEntity extends object, PrimaryKey extends keyof TEntity & string>(this: object, record: TEntity, repository: BaseRepository<TEntity, PrimaryKey>, exists?: boolean): Model<TEntity, PrimaryKey>;
|
|
121
133
|
static boot(): void;
|
|
122
134
|
static observe(this: object, observer: ModelObserver): void;
|
|
123
135
|
static addGlobalScope<TEntity extends object, PrimaryKey extends keyof TEntity & string>(this: object, _name: string, scope: GlobalScopeFn<TEntity, PrimaryKey>): void;
|
|
124
136
|
static repository<TEntity extends object, PrimaryKey extends keyof TEntity & string>(this: object): BaseRepository<TEntity, PrimaryKey>;
|
|
125
|
-
static query(this:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
static
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
static
|
|
132
|
-
|
|
137
|
+
static query<TModel extends object>(this: {
|
|
138
|
+
prototype: TModel;
|
|
139
|
+
}): ModelQuery<TModel>;
|
|
140
|
+
static newFromRecord<TModel extends object>(this: {
|
|
141
|
+
prototype: TModel;
|
|
142
|
+
}, record: object, exists?: boolean): TModel;
|
|
143
|
+
static create<TModel extends object>(this: {
|
|
144
|
+
prototype: TModel;
|
|
145
|
+
}, attributes: Record<string, unknown>, forced?: Record<string, unknown>): Promise<TModel>;
|
|
146
|
+
static with<TModel extends object>(this: {
|
|
147
|
+
prototype: TModel;
|
|
148
|
+
}, ...relations: RelationNameInput[]): ModelQuery<TModel>;
|
|
149
|
+
static withTrashed<TModel extends object>(this: {
|
|
150
|
+
prototype: TModel;
|
|
151
|
+
}): ModelQuery<TModel>;
|
|
152
|
+
static onlyTrashed<TModel extends object>(this: {
|
|
153
|
+
prototype: TModel;
|
|
154
|
+
}): ModelQuery<TModel>;
|
|
155
|
+
static chunk<TModel extends object>(this: {
|
|
156
|
+
prototype: TModel;
|
|
157
|
+
}, count: number, callback: (models: TModel[]) => Promise<boolean | void>): Promise<void>;
|
|
158
|
+
static cursorPaginate<TModel extends object>(this: {
|
|
159
|
+
prototype: TModel;
|
|
160
|
+
}, options: {
|
|
133
161
|
perPage: number;
|
|
134
162
|
cursor?: unknown;
|
|
135
163
|
}): Promise<{
|
|
136
|
-
data:
|
|
164
|
+
data: TModel[];
|
|
137
165
|
meta: {
|
|
138
166
|
per_page: number;
|
|
139
167
|
next_cursor: unknown;
|
|
@@ -141,22 +169,46 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
|
|
|
141
169
|
has_more: boolean;
|
|
142
170
|
};
|
|
143
171
|
}>;
|
|
144
|
-
static whereHas
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
static
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
static
|
|
151
|
-
|
|
172
|
+
static whereHas<TModel extends object>(this: {
|
|
173
|
+
prototype: TModel;
|
|
174
|
+
}, name: string, constrain?: (query: AnyRelationQuery) => void): ModelQuery<TModel>;
|
|
175
|
+
static has<TModel extends object>(this: {
|
|
176
|
+
prototype: TModel;
|
|
177
|
+
}, name: string): ModelQuery<TModel>;
|
|
178
|
+
static doesntHave<TModel extends object>(this: {
|
|
179
|
+
prototype: TModel;
|
|
180
|
+
}, name: string): ModelQuery<TModel>;
|
|
181
|
+
static whereDoesntHave<TModel extends object>(this: {
|
|
182
|
+
prototype: TModel;
|
|
183
|
+
}, name: string, constrain?: (query: AnyRelationQuery) => void): ModelQuery<TModel>;
|
|
184
|
+
static find<TModel extends object>(this: {
|
|
185
|
+
prototype: TModel;
|
|
186
|
+
}, id: unknown): Promise<TModel | null>;
|
|
187
|
+
static findOrFail<TModel extends object>(this: {
|
|
188
|
+
prototype: TModel;
|
|
189
|
+
}, id: unknown, errorFactory?: (id: unknown) => Error): Promise<TModel>;
|
|
190
|
+
static all<TModel extends object>(this: {
|
|
191
|
+
prototype: TModel;
|
|
192
|
+
}, options?: Omit<QueryOptions<object>, "where">): Promise<TModel[]>;
|
|
193
|
+
static where<TModel extends object>(this: {
|
|
194
|
+
prototype: TModel;
|
|
195
|
+
}, where: QueryWhere<object>): ModelQuery<TModel>;
|
|
152
196
|
static count(this: object): Promise<number>;
|
|
153
197
|
static pluck(this: object, column: string): Promise<unknown[]>;
|
|
154
198
|
static pluck(this: object, column: string, keyBy: string): Promise<Map<unknown, unknown>>;
|
|
155
199
|
static value(this: object, column: string): Promise<unknown>;
|
|
156
|
-
static firstWhere
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
static
|
|
200
|
+
static firstWhere<TModel extends object>(this: {
|
|
201
|
+
prototype: TModel;
|
|
202
|
+
}, where: QueryWhere<object>, options?: Omit<QueryOptions<object>, "where">): Promise<TModel | null>;
|
|
203
|
+
static firstOrNew<TModel extends object>(this: {
|
|
204
|
+
prototype: TModel;
|
|
205
|
+
}, where: QueryWhere<object>, values?: Record<string, unknown>): Promise<TModel>;
|
|
206
|
+
static firstOrCreate<TModel extends object>(this: {
|
|
207
|
+
prototype: TModel;
|
|
208
|
+
}, where: QueryWhere<object>, values?: Record<string, unknown>): Promise<TModel>;
|
|
209
|
+
static updateOrCreate<TModel extends object>(this: {
|
|
210
|
+
prototype: TModel;
|
|
211
|
+
}, where: QueryWhere<object>, values?: Record<string, unknown>): Promise<TModel>;
|
|
160
212
|
save(): Promise<this>;
|
|
161
213
|
update(changes: Partial<TEntity>): Promise<this>;
|
|
162
214
|
delete(): Promise<boolean>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type Middleware = (request: Request, next: () => Promise<Response>) => Promise<Response>;
|
|
2
|
-
type RouteHandler = (request:
|
|
3
|
-
declare function composeMiddleware(...middleware: Middleware[]): (handler: RouteHandler) => RouteHandler
|
|
2
|
+
type RouteHandler<TRequest extends Request = Request> = (request: TRequest) => Response | Promise<Response>;
|
|
3
|
+
declare function composeMiddleware(...middleware: Middleware[]): <TRequest extends Request = Request>(handler: RouteHandler<TRequest>) => RouteHandler<TRequest>;
|
|
4
4
|
declare function requestIdMiddleware(request: Request, next: () => Promise<Response>): Promise<Response>;
|
|
5
5
|
declare function wrapRouteHandler<T>(handler: T, middleware: Middleware[]): T;
|
|
6
6
|
declare function applyMiddlewareToRoutes<T extends Record<string, unknown>>(routes: T, middleware: Middleware[]): T;
|
|
@@ -3,7 +3,9 @@ declare function jsonResponse(data: unknown, init?: ResponseInit): Response;
|
|
|
3
3
|
declare function createdResponse(data: unknown, init?: ResponseInit): Response;
|
|
4
4
|
declare function noContentResponse(): Response;
|
|
5
5
|
declare function errorResponse(error: unknown): Response;
|
|
6
|
+
declare function withErrorHandling<THandler extends (request: Request) => Response | Promise<Response>>(handler: THandler): (...args: Parameters<THandler>) => Promise<Response>;
|
|
6
7
|
declare function withErrorHandling<TArgs extends unknown[]>(handler: (...args: TArgs) => Response | Promise<Response>): (...args: TArgs) => Promise<Response>;
|
|
8
|
+
declare function withJsonErrorHandling<THandler extends (request: Request) => Response | Promise<Response>>(handler: THandler): (...args: Parameters<THandler>) => Promise<Response>;
|
|
7
9
|
declare function withJsonErrorHandling<TArgs extends unknown[]>(handler: (...args: TArgs) => Response | Promise<Response>): (...args: TArgs) => Promise<Response>;
|
|
8
10
|
declare function createJsonErrorMiddleware(): Middleware;
|
|
9
11
|
declare function createWebErrorMiddleware(): Middleware;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import { type Middleware
|
|
2
|
-
|
|
1
|
+
import { type Middleware } from "./middleware";
|
|
2
|
+
/** Middleware consumes native Request; composition preserves the handler's request subtype. */
|
|
3
|
+
declare function withMiddleware(...middleware: Middleware[]): <TRequest extends Request = Request>(handler: import("./middleware").RouteHandler<TRequest>) => import("./middleware").RouteHandler<TRequest>;
|
|
3
4
|
export { withMiddleware };
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/contracts/container.ts
|
|
3
|
+
function createServiceToken(key) {
|
|
4
|
+
if (!key.trim())
|
|
5
|
+
throw new Error("Service token key must not be empty.");
|
|
6
|
+
return key;
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
class ServiceContainer {
|
|
4
10
|
services = new Map;
|
|
5
11
|
singletonFactories = new Map;
|
|
@@ -71,5 +77,6 @@ class ConfigStore {
|
|
|
71
77
|
}
|
|
72
78
|
export {
|
|
73
79
|
ConfigStore,
|
|
74
|
-
ServiceContainer
|
|
80
|
+
ServiceContainer,
|
|
81
|
+
createServiceToken
|
|
75
82
|
};
|
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
// ../../src/core/database/model.ts
|
|
3
3
|
import { ConflictError, NotFoundError } from "@getstrata/core/errors/http";
|
|
4
4
|
|
|
5
|
+
// ../../src/core/auth/password.ts
|
|
6
|
+
async function hashPassword(password) {
|
|
7
|
+
return await Bun.password.hash(password, {
|
|
8
|
+
algorithm: "bcrypt",
|
|
9
|
+
cost: 12
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
async function verifyPassword(password, passwordHash) {
|
|
13
|
+
return await Bun.password.verify(password, passwordHash);
|
|
14
|
+
}
|
|
15
|
+
|
|
5
16
|
// ../../src/core/database/inflection.ts
|
|
6
17
|
function singularize(word) {
|
|
7
18
|
if (word.endsWith("ies") && word.length > 3) {
|
|
@@ -1624,12 +1635,14 @@ var PASSWORD_HASH_PATTERN = /^\$(?:2[aby]?|argon2(?:i|d|id)?)\$/;
|
|
|
1624
1635
|
function isAlreadyHashed(value) {
|
|
1625
1636
|
return PASSWORD_HASH_PATTERN.test(value);
|
|
1626
1637
|
}
|
|
1627
|
-
function hashCastValue(value) {
|
|
1638
|
+
async function hashCastValue(value) {
|
|
1639
|
+
if (value === null || value === undefined)
|
|
1640
|
+
return value;
|
|
1628
1641
|
const plain = String(value);
|
|
1629
1642
|
if (isAlreadyHashed(plain)) {
|
|
1630
1643
|
return plain;
|
|
1631
1644
|
}
|
|
1632
|
-
return
|
|
1645
|
+
return await hashPassword(plain);
|
|
1633
1646
|
}
|
|
1634
1647
|
function hydrateValue(value, cast) {
|
|
1635
1648
|
if (value === null || value === undefined) {
|
|
@@ -1654,6 +1667,8 @@ function hydrateValue(value, cast) {
|
|
|
1654
1667
|
}
|
|
1655
1668
|
}
|
|
1656
1669
|
function dehydrateValue(value, cast) {
|
|
1670
|
+
if (cast === "hashed")
|
|
1671
|
+
return hashCastValue(value);
|
|
1657
1672
|
if (value === null || value === undefined) {
|
|
1658
1673
|
return value;
|
|
1659
1674
|
}
|
|
@@ -1669,8 +1684,6 @@ function dehydrateValue(value, cast) {
|
|
|
1669
1684
|
case "integer":
|
|
1670
1685
|
case "int":
|
|
1671
1686
|
return value === "" ? null : Number(value);
|
|
1672
|
-
case "hashed":
|
|
1673
|
-
return hashCastValue(value);
|
|
1674
1687
|
default:
|
|
1675
1688
|
return value;
|
|
1676
1689
|
}
|
|
@@ -1691,17 +1704,25 @@ function filterMassAssignable(fillable, guarded, input) {
|
|
|
1691
1704
|
return Object.fromEntries(Object.entries(input).filter(([key]) => !blocked.has(key)));
|
|
1692
1705
|
}
|
|
1693
1706
|
function applyCasts(values, casts, direction) {
|
|
1694
|
-
|
|
1695
|
-
|
|
1707
|
+
const entries = Object.entries(casts);
|
|
1708
|
+
if (entries.length === 0) {
|
|
1709
|
+
return direction === "hydrate" ? values : Promise.resolve(values);
|
|
1696
1710
|
}
|
|
1697
1711
|
const result = { ...values };
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1712
|
+
if (direction === "hydrate") {
|
|
1713
|
+
for (const [key, cast] of entries) {
|
|
1714
|
+
if (key in result && cast)
|
|
1715
|
+
result[key] = hydrateValue(result[key], cast);
|
|
1702
1716
|
}
|
|
1717
|
+
return result;
|
|
1703
1718
|
}
|
|
1704
|
-
return
|
|
1719
|
+
return (async () => {
|
|
1720
|
+
for (const [key, cast] of entries) {
|
|
1721
|
+
if (key in result && cast)
|
|
1722
|
+
result[key] = await dehydrateValue(result[key], cast);
|
|
1723
|
+
}
|
|
1724
|
+
return result;
|
|
1725
|
+
})();
|
|
1705
1726
|
}
|
|
1706
1727
|
function castPluckedValue(modelClass, column, value) {
|
|
1707
1728
|
const cast = modelStatics(modelClass).$casts?.[column];
|
|
@@ -2096,7 +2117,7 @@ class Model {
|
|
|
2096
2117
|
...forced
|
|
2097
2118
|
};
|
|
2098
2119
|
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
2099
|
-
const payload = statics.dehydrateAttributes(withTimestamps);
|
|
2120
|
+
const payload = await statics.dehydrateAttributes(withTimestamps);
|
|
2100
2121
|
const pending = statics.newFromRecord({ ...payload }, false);
|
|
2101
2122
|
if (await runObservers(pending, "saving") === false) {
|
|
2102
2123
|
throw new Error(`${this.name}.create() was cancelled by an observer.`);
|
|
@@ -2237,7 +2258,7 @@ class Model {
|
|
|
2237
2258
|
return this;
|
|
2238
2259
|
}
|
|
2239
2260
|
if (updating) {
|
|
2240
|
-
const changes = applyTimestampsOnUpdate(table.columns, applyCasts(this.attributes, casts, "dehydrate"), timestamps);
|
|
2261
|
+
const changes = applyTimestampsOnUpdate(table.columns, await applyCasts(this.attributes, casts, "dehydrate"), timestamps);
|
|
2241
2262
|
const record = await this.repository.updateByIdOrThrow(this.id, changes);
|
|
2242
2263
|
this.attributes = ModelClass.hydrateAttributes(record);
|
|
2243
2264
|
await runObservers(this, "updated");
|
|
@@ -2246,7 +2267,7 @@ class Model {
|
|
|
2246
2267
|
}
|
|
2247
2268
|
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
|
|
2248
2269
|
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
2249
|
-
const payload = ModelClass.dehydrateAttributes(withTimestamps);
|
|
2270
|
+
const payload = await ModelClass.dehydrateAttributes(withTimestamps);
|
|
2250
2271
|
const record = await this.repository.create(payload);
|
|
2251
2272
|
this.attributes = ModelClass.hydrateAttributes(record);
|
|
2252
2273
|
this._exists = true;
|
|
@@ -62,10 +62,7 @@ function composeMiddleware(...middleware) {
|
|
|
62
62
|
|
|
63
63
|
// ../../src/core/http/routeMiddleware.ts
|
|
64
64
|
function withMiddleware(...middleware) {
|
|
65
|
-
|
|
66
|
-
return (handler) => {
|
|
67
|
-
return wrap(handler);
|
|
68
|
-
};
|
|
65
|
+
return composeMiddleware(...middleware);
|
|
69
66
|
}
|
|
70
67
|
export {
|
|
71
68
|
withMiddleware
|
|
@@ -29,7 +29,7 @@ export { createTokenAbilityChecker } from "../core/auth/tokenAbilityChecker.ts";
|
|
|
29
29
|
export { type CacheDriver, type CreateCacheStoreOptions, createCacheStore, } from "../core/cache/createCacheStore.ts";
|
|
30
30
|
export { default as CacheRepository } from "../core/cache/repository.ts";
|
|
31
31
|
export { CACHE_TAGS } from "../core/cache/tags.ts";
|
|
32
|
-
export { ConfigStore, ServiceContainer } from "../core/contracts/container.ts";
|
|
32
|
+
export { ConfigStore, createServiceToken, ServiceContainer, type ServiceToken, } from "../core/contracts/container.ts";
|
|
33
33
|
export type { ServiceProvider } from "../core/contracts/di.ts";
|
|
34
34
|
export { resolveService } from "../core/contracts/di.ts";
|
|
35
35
|
export type { DatabaseConnection } from "../core/database/baseRepository.ts";
|
package/dist/index.js
CHANGED
|
@@ -1124,6 +1124,12 @@ function readBasicCredentials(request) {
|
|
|
1124
1124
|
}
|
|
1125
1125
|
|
|
1126
1126
|
// ../../src/core/auth/password.ts
|
|
1127
|
+
async function hashPassword(password) {
|
|
1128
|
+
return await Bun.password.hash(password, {
|
|
1129
|
+
algorithm: "bcrypt",
|
|
1130
|
+
cost: 12
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1127
1133
|
async function verifyPassword(password, passwordHash) {
|
|
1128
1134
|
return await Bun.password.verify(password, passwordHash);
|
|
1129
1135
|
}
|
|
@@ -3760,6 +3766,12 @@ var CACHE_TAGS = {
|
|
|
3760
3766
|
offers: "offers"
|
|
3761
3767
|
};
|
|
3762
3768
|
// ../../src/core/contracts/container.ts
|
|
3769
|
+
function createServiceToken(key) {
|
|
3770
|
+
if (!key.trim())
|
|
3771
|
+
throw new Error("Service token key must not be empty.");
|
|
3772
|
+
return key;
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3763
3775
|
class ServiceContainer {
|
|
3764
3776
|
services = new Map;
|
|
3765
3777
|
singletonFactories = new Map;
|
|
@@ -6604,12 +6616,14 @@ var PASSWORD_HASH_PATTERN = /^\$(?:2[aby]?|argon2(?:i|d|id)?)\$/;
|
|
|
6604
6616
|
function isAlreadyHashed(value) {
|
|
6605
6617
|
return PASSWORD_HASH_PATTERN.test(value);
|
|
6606
6618
|
}
|
|
6607
|
-
function hashCastValue(value) {
|
|
6619
|
+
async function hashCastValue(value) {
|
|
6620
|
+
if (value === null || value === undefined)
|
|
6621
|
+
return value;
|
|
6608
6622
|
const plain = String(value);
|
|
6609
6623
|
if (isAlreadyHashed(plain)) {
|
|
6610
6624
|
return plain;
|
|
6611
6625
|
}
|
|
6612
|
-
return
|
|
6626
|
+
return await hashPassword(plain);
|
|
6613
6627
|
}
|
|
6614
6628
|
function hydrateValue(value, cast) {
|
|
6615
6629
|
if (value === null || value === undefined) {
|
|
@@ -6634,6 +6648,8 @@ function hydrateValue(value, cast) {
|
|
|
6634
6648
|
}
|
|
6635
6649
|
}
|
|
6636
6650
|
function dehydrateValue(value, cast) {
|
|
6651
|
+
if (cast === "hashed")
|
|
6652
|
+
return hashCastValue(value);
|
|
6637
6653
|
if (value === null || value === undefined) {
|
|
6638
6654
|
return value;
|
|
6639
6655
|
}
|
|
@@ -6649,8 +6665,6 @@ function dehydrateValue(value, cast) {
|
|
|
6649
6665
|
case "integer":
|
|
6650
6666
|
case "int":
|
|
6651
6667
|
return value === "" ? null : Number(value);
|
|
6652
|
-
case "hashed":
|
|
6653
|
-
return hashCastValue(value);
|
|
6654
6668
|
default:
|
|
6655
6669
|
return value;
|
|
6656
6670
|
}
|
|
@@ -6671,17 +6685,25 @@ function filterMassAssignable(fillable, guarded, input) {
|
|
|
6671
6685
|
return Object.fromEntries(Object.entries(input).filter(([key]) => !blocked.has(key)));
|
|
6672
6686
|
}
|
|
6673
6687
|
function applyCasts(values, casts, direction) {
|
|
6674
|
-
|
|
6675
|
-
|
|
6688
|
+
const entries = Object.entries(casts);
|
|
6689
|
+
if (entries.length === 0) {
|
|
6690
|
+
return direction === "hydrate" ? values : Promise.resolve(values);
|
|
6676
6691
|
}
|
|
6677
6692
|
const result = { ...values };
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6693
|
+
if (direction === "hydrate") {
|
|
6694
|
+
for (const [key, cast] of entries) {
|
|
6695
|
+
if (key in result && cast)
|
|
6696
|
+
result[key] = hydrateValue(result[key], cast);
|
|
6682
6697
|
}
|
|
6698
|
+
return result;
|
|
6683
6699
|
}
|
|
6684
|
-
return
|
|
6700
|
+
return (async () => {
|
|
6701
|
+
for (const [key, cast] of entries) {
|
|
6702
|
+
if (key in result && cast)
|
|
6703
|
+
result[key] = await dehydrateValue(result[key], cast);
|
|
6704
|
+
}
|
|
6705
|
+
return result;
|
|
6706
|
+
})();
|
|
6685
6707
|
}
|
|
6686
6708
|
function castPluckedValue(modelClass, column, value) {
|
|
6687
6709
|
const cast = modelStatics(modelClass).$casts?.[column];
|
|
@@ -7076,7 +7098,7 @@ class Model {
|
|
|
7076
7098
|
...forced
|
|
7077
7099
|
};
|
|
7078
7100
|
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
7079
|
-
const payload = statics.dehydrateAttributes(withTimestamps);
|
|
7101
|
+
const payload = await statics.dehydrateAttributes(withTimestamps);
|
|
7080
7102
|
const pending = statics.newFromRecord({ ...payload }, false);
|
|
7081
7103
|
if (await runObservers(pending, "saving") === false) {
|
|
7082
7104
|
throw new Error(`${this.name}.create() was cancelled by an observer.`);
|
|
@@ -7217,7 +7239,7 @@ class Model {
|
|
|
7217
7239
|
return this;
|
|
7218
7240
|
}
|
|
7219
7241
|
if (updating) {
|
|
7220
|
-
const changes = applyTimestampsOnUpdate(table.columns, applyCasts(this.attributes, casts, "dehydrate"), timestamps);
|
|
7242
|
+
const changes = applyTimestampsOnUpdate(table.columns, await applyCasts(this.attributes, casts, "dehydrate"), timestamps);
|
|
7221
7243
|
const record = await this.repository.updateByIdOrThrow(this.id, changes);
|
|
7222
7244
|
this.attributes = ModelClass.hydrateAttributes(record);
|
|
7223
7245
|
await runObservers(this, "updated");
|
|
@@ -7226,7 +7248,7 @@ class Model {
|
|
|
7226
7248
|
}
|
|
7227
7249
|
const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
|
|
7228
7250
|
const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
|
|
7229
|
-
const payload = ModelClass.dehydrateAttributes(withTimestamps);
|
|
7251
|
+
const payload = await ModelClass.dehydrateAttributes(withTimestamps);
|
|
7230
7252
|
const record = await this.repository.create(payload);
|
|
7231
7253
|
this.attributes = ModelClass.hydrateAttributes(record);
|
|
7232
7254
|
this._exists = true;
|
|
@@ -10617,10 +10639,7 @@ function getRouteParams(request) {
|
|
|
10617
10639
|
}
|
|
10618
10640
|
// ../../src/core/http/routeMiddleware.ts
|
|
10619
10641
|
function withMiddleware(...middleware) {
|
|
10620
|
-
|
|
10621
|
-
return (handler) => {
|
|
10622
|
-
return wrap(handler);
|
|
10623
|
-
};
|
|
10642
|
+
return composeMiddleware(...middleware);
|
|
10624
10643
|
}
|
|
10625
10644
|
// ../../src/core/http/routeModelBinding.ts
|
|
10626
10645
|
function bindRouteModel(param, resolver, handler) {
|
|
@@ -13179,6 +13198,7 @@ export {
|
|
|
13179
13198
|
createScimAuthMiddleware,
|
|
13180
13199
|
createScimThrottleMiddleware,
|
|
13181
13200
|
createSecurityHeadersMiddleware,
|
|
13201
|
+
createServiceToken,
|
|
13182
13202
|
createSqliteConnection,
|
|
13183
13203
|
createStorageDriver,
|
|
13184
13204
|
createTenantMiddleware,
|