@getstrata/core 0.5.91 → 0.5.98

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.
@@ -112,7 +112,16 @@ function buildWhereClause(tableName, where = {}) {
112
112
  params
113
113
  };
114
114
  }
115
+ function remapExistsSql(sql, existsParams, params) {
116
+ const offset = params.length;
117
+ params.push(...existsParams);
118
+ return sql.replace(/\$(\d+)/g, (_match, index) => `$${offset + Number(index)}`);
119
+ }
115
120
  function buildWhereNodeClause(tableName, node, params) {
121
+ if ("exists" in node) {
122
+ const body = remapExistsSql(node.exists.sql, node.exists.params, params);
123
+ return `${node.exists.not ? "NOT " : ""}EXISTS (${body})`;
124
+ }
116
125
  if ("where" in node) {
117
126
  return appendWhereParts(tableName, node.where, params);
118
127
  }
@@ -112,7 +112,16 @@ function buildWhereClause(tableName, where = {}) {
112
112
  params
113
113
  };
114
114
  }
115
+ function remapExistsSql(sql, existsParams, params) {
116
+ const offset = params.length;
117
+ params.push(...existsParams);
118
+ return sql.replace(/\$(\d+)/g, (_match, index) => `$${offset + Number(index)}`);
119
+ }
115
120
  function buildWhereNodeClause(tableName, node, params) {
121
+ if ("exists" in node) {
122
+ const body = remapExistsSql(node.exists.sql, node.exists.params, params);
123
+ return `${node.exists.not ? "NOT " : ""}EXISTS (${body})`;
124
+ }
116
125
  if ("where" in node) {
117
126
  return appendWhereParts(tableName, node.where, params);
118
127
  }
@@ -484,6 +493,23 @@ class RepositoryQuery {
484
493
  this.queryOptions = { ...this.queryOptions, limit };
485
494
  return this;
486
495
  }
496
+ whereNull(column) {
497
+ return this.where({ [column]: null });
498
+ }
499
+ whereNotNull(column) {
500
+ return this.where({ [column]: { isNull: false } });
501
+ }
502
+ whereIn(column, values) {
503
+ return this.where({ [column]: values });
504
+ }
505
+ whereExists(sql, params = []) {
506
+ this.whereNodes.push({ kind: "and", exists: { sql, params } });
507
+ return this;
508
+ }
509
+ whereNotExists(sql, params = []) {
510
+ this.whereNodes.push({ kind: "and", exists: { sql, params, not: true } });
511
+ return this;
512
+ }
487
513
  offset(offset) {
488
514
  this.queryOptions = { ...this.queryOptions, offset };
489
515
  return this;
@@ -379,7 +379,16 @@ function buildWhereClause(tableName, where = {}) {
379
379
  params
380
380
  };
381
381
  }
382
+ function remapExistsSql(sql, existsParams, params) {
383
+ const offset = params.length;
384
+ params.push(...existsParams);
385
+ return sql.replace(/\$(\d+)/g, (_match, index) => `$${offset + Number(index)}`);
386
+ }
382
387
  function buildWhereNodeClause(tableName, node, params) {
388
+ if ("exists" in node) {
389
+ const body = remapExistsSql(node.exists.sql, node.exists.params, params);
390
+ return `${node.exists.not ? "NOT " : ""}EXISTS (${body})`;
391
+ }
383
392
  if ("where" in node) {
384
393
  return appendWhereParts(tableName, node.where, params);
385
394
  }
@@ -3,6 +3,64 @@
3
3
  function serializeDate(value) {
4
4
  return value instanceof Date ? value.toISOString() : value;
5
5
  }
6
+ function whenLoaded(model, relation, transform) {
7
+ const value = model.loaded(relation);
8
+ if (value === undefined) {
9
+ return;
10
+ }
11
+ return transform ? transform(value) : value;
12
+ }
13
+
14
+ class JsonResource {
15
+ resource;
16
+ static wrap = "data";
17
+ extra = {};
18
+ constructor(resource) {
19
+ this.resource = resource;
20
+ }
21
+ static make(resource) {
22
+ return new JsonResource(resource);
23
+ }
24
+ static collection(items) {
25
+ return new ResourceCollection(items);
26
+ }
27
+ additional(data) {
28
+ this.extra = { ...this.extra, ...data };
29
+ return this;
30
+ }
31
+ when(condition, value) {
32
+ return condition ? value : undefined;
33
+ }
34
+ whenLoaded(relation, transform) {
35
+ const model = this.resource;
36
+ if (typeof model.loaded !== "function") {
37
+ return;
38
+ }
39
+ return whenLoaded(model, relation, transform);
40
+ }
41
+ toArray() {
42
+ if (this.resource && typeof this.resource === "object" && "toArray" in this.resource) {
43
+ return this.resource.toArray();
44
+ }
45
+ return { ...this.resource };
46
+ }
47
+ toResponse() {
48
+ const wrap = this.constructor.wrap;
49
+ const payload = this.toArray();
50
+ if (wrap === null) {
51
+ return { ...payload, ...this.extra };
52
+ }
53
+ return { [wrap]: payload, ...this.extra };
54
+ }
55
+ }
56
+
57
+ class ResourceCollection extends JsonResource {
58
+ toArray() {
59
+ return {
60
+ data: this.resource.map((item) => item instanceof JsonResource ? item.toArray() : { ...item })
61
+ };
62
+ }
63
+ }
6
64
  function toResourceCollection(items, transformer) {
7
65
  return items.map(transformer);
8
66
  }
@@ -13,7 +71,10 @@ function toPaginatedResourceCollection(items, meta, transformer) {
13
71
  };
14
72
  }
15
73
  export {
74
+ JsonResource,
75
+ ResourceCollection,
16
76
  serializeDate,
17
77
  toPaginatedResourceCollection,
18
- toResourceCollection
78
+ toResourceCollection,
79
+ whenLoaded
19
80
  };
@@ -1,12 +1,12 @@
1
1
  // @bun
2
2
  // ../../src/core/http/response.ts
3
- import { HttpError as HttpError3 } from "@getstrata/core/errors/http";
3
+ import { toHttpError as toHttpError3 } from "@getstrata/core/errors/http";
4
4
 
5
5
  // ../../src/core/database/errors.ts
6
6
  import {
7
7
  BadRequestError,
8
8
  ConflictError,
9
- HttpError,
9
+ toHttpError,
10
10
  UnprocessableEntityError
11
11
  } from "@getstrata/core/errors/http";
12
12
  function isPostgresError(error) {
@@ -25,8 +25,9 @@ function getPostgresSqlState(error) {
25
25
  return;
26
26
  }
27
27
  function mapDatabaseError(error) {
28
- if (error instanceof HttpError) {
29
- return error;
28
+ const httpError = toHttpError(error);
29
+ if (httpError) {
30
+ return httpError;
30
31
  }
31
32
  if (!isPostgresError(error)) {
32
33
  const message = error instanceof Error ? error.message : "Database operation failed.";
@@ -66,7 +67,7 @@ async function withDatabaseErrorHandling(operation) {
66
67
  }
67
68
 
68
69
  // ../../src/core/http/webErrorResponse.ts
69
- import { HttpError as HttpError2, UnauthorizedError, ValidationError } from "@getstrata/core/errors/http";
70
+ import { toHttpError as toHttpError2, ValidationError } from "@getstrata/core/errors/http";
70
71
 
71
72
  // ../../src/core/runtime/frontendMode.ts
72
73
  function readFrontendMode() {
@@ -301,11 +302,11 @@ async function webErrorResponse(error, request) {
301
302
  if (!request || !isViewsEnabled() || requestPrefersJson(request)) {
302
303
  return null;
303
304
  }
304
- const mappedError = error instanceof HttpError2 ? error : mapDatabaseError(error);
305
- if (mappedError instanceof UnauthorizedError) {
305
+ const mappedError = toHttpError2(error) ?? mapDatabaseError(error);
306
+ if (mappedError.status === 401) {
306
307
  return Response.redirect(loginRedirectLocation(request), 302);
307
308
  }
308
- const errors = mappedError instanceof ValidationError ? normalizeFieldErrors(mappedError.details) : undefined;
309
+ const errors = mappedError instanceof ValidationError || mappedError.name === "ValidationError" ? normalizeFieldErrors(mappedError.details) : undefined;
309
310
  return htmlErrorResponse({
310
311
  status: mappedError.status,
311
312
  title: errorPageTitle(mappedError.status, mappedError.message),
@@ -329,7 +330,7 @@ function noContentResponse() {
329
330
  return new Response(null, { status: 204 });
330
331
  }
331
332
  function errorResponse(error) {
332
- const mappedError = error instanceof HttpError3 ? error : mapDatabaseError(error);
333
+ const mappedError = toHttpError3(error) ?? mapDatabaseError(error);
333
334
  return Response.json({
334
335
  error: mappedError.message,
335
336
  ...mappedError.details === undefined ? {} : { details: mappedError.details }
@@ -1,12 +1,12 @@
1
1
  // @bun
2
2
  // ../../src/core/http/webErrorResponse.ts
3
- import { HttpError as HttpError2, UnauthorizedError, ValidationError } from "@getstrata/core/errors/http";
3
+ import { toHttpError as toHttpError2, ValidationError } from "@getstrata/core/errors/http";
4
4
 
5
5
  // ../../src/core/database/errors.ts
6
6
  import {
7
7
  BadRequestError,
8
8
  ConflictError,
9
- HttpError,
9
+ toHttpError,
10
10
  UnprocessableEntityError
11
11
  } from "@getstrata/core/errors/http";
12
12
  function isPostgresError(error) {
@@ -25,8 +25,9 @@ function getPostgresSqlState(error) {
25
25
  return;
26
26
  }
27
27
  function mapDatabaseError(error) {
28
- if (error instanceof HttpError) {
29
- return error;
28
+ const httpError = toHttpError(error);
29
+ if (httpError) {
30
+ return httpError;
30
31
  }
31
32
  if (!isPostgresError(error)) {
32
33
  const message = error instanceof Error ? error.message : "Database operation failed.";
@@ -298,11 +299,11 @@ async function webErrorResponse(error, request) {
298
299
  if (!request || !isViewsEnabled() || requestPrefersJson(request)) {
299
300
  return null;
300
301
  }
301
- const mappedError = error instanceof HttpError2 ? error : mapDatabaseError(error);
302
- if (mappedError instanceof UnauthorizedError) {
302
+ const mappedError = toHttpError2(error) ?? mapDatabaseError(error);
303
+ if (mappedError.status === 401) {
303
304
  return Response.redirect(loginRedirectLocation(request), 302);
304
305
  }
305
- const errors = mappedError instanceof ValidationError ? normalizeFieldErrors(mappedError.details) : undefined;
306
+ const errors = mappedError instanceof ValidationError || mappedError.name === "ValidationError" ? normalizeFieldErrors(mappedError.details) : undefined;
306
307
  return htmlErrorResponse({
307
308
  status: mappedError.status,
308
309
  title: errorPageTitle(mappedError.status, mappedError.message),
@@ -10,7 +10,7 @@ export type { AuthUser } from "../core/auth/authContext.ts";
10
10
  export { authContext, currentAuthUser, runWithAuthUser } from "../core/auth/authContext.ts";
11
11
  export type { AuthGuard } from "../core/auth/guard.ts";
12
12
  export { ApiTokenGuard, AuthManager, CompositeGuard, DatabaseTokenGuard, GuestGuard, } from "../core/auth/guard.ts";
13
- export { configureMembershipLookup, currentOrganizationIds, currentOrgRole, hasMinimumOrgRole, hasOrgMembership, } from "../core/auth/membershipContext.ts";
13
+ export { configureMembershipLookup, currentOrganizationIds, currentOrgRole, hasMinimumOrgRole, hasOrgMembership, resolveMembershipLookup, runWithMembershipContext, } from "../core/auth/membershipContext.ts";
14
14
  export { createMembershipMiddleware } from "../core/auth/membershipMiddleware.ts";
15
15
  export { appendOrganizationScope, appendProjectScope, assertOrganizationReadable, assertResourceInCurrentTenant, emptyPaginateResult, resolveOrganizationScope, scopedOrganizationIds, } from "../core/auth/membershipScope.ts";
16
16
  export { default as MembershipService, resolveMembershipService, } from "../core/auth/membershipService.ts";
@@ -29,11 +29,13 @@ export { bindBunSql, createBunSqlPool } from "../core/database/bunSql.ts";
29
29
  export { createDatabaseConnection } from "../core/database/connection.ts";
30
30
  export { getActiveDatabaseConnection, runWithDatabaseConnection, } from "../core/database/connectionContext.ts";
31
31
  export { getDefaultDatabasePool, getDefaultDatabaseQuery, registerDefaultDatabasePool, } from "../core/database/defaultConnection.ts";
32
+ export { Factory } from "../core/database/factory.ts";
33
+ export { foreignKeyFromTable, pivotTableName, singularize } from "../core/database/inflection.ts";
32
34
  export { withMigrationLock } from "../core/database/migrations/advisoryLock.ts";
33
35
  export { freshDatabase, getMigrationStatus, loadMigrationsFromDirectory, migrateDatabase, rollbackDatabase, } from "../core/database/migrations/runner.ts";
34
36
  export type { Migration, MigrationDatabase, MigrationStatus, } from "../core/database/migrations/types.ts";
35
37
  export type { CastType, GlobalScopeFn, ModelConstructor } from "../core/database/model.ts";
36
- export { applyCasts, dehydrateValue, filterMassAssignable, hydrateValue, Model, registerModelRepository, } from "../core/database/model.ts";
38
+ export { applyCasts, BelongsToManyRelationQuery, BelongsToRelationQuery, dehydrateValue, filterMassAssignable, HasManyRelationQuery, HasOneRelationQuery, hydrateValue, Model, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, registerModelRepository, } from "../core/database/model.ts";
37
39
  export { createDatabaseQueryProxy } from "../core/database/queryProxy.ts";
38
40
  export type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation, } from "../core/database/relationships.ts";
39
41
  export { belongsTo, belongsToMany, hasMany, hasOne, indexBelongsToManyRelation, indexBelongsToRelation, indexHasManyRelation, indexHasOneRelation, indexMorphManyRelation, indexMorphOneRelation, indexMorphToRelation, morphMany, morphOne, morphTo, } from "../core/database/relationships.ts";
@@ -48,7 +50,7 @@ export { runInTransaction } from "../core/database/transaction.ts";
48
50
  export type { QueryJoin, QueryJoinOn, QueryOptions, QueryOrder, QuerySelectItem, QueryWhere, } from "../core/database/types.ts";
49
51
  export type { WhereNode } from "../core/database/whereBuilder.ts";
50
52
  export { WhereBuilder } from "../core/database/whereBuilder.ts";
51
- export { BadRequestError, ConflictError, ForbiddenError, HttpError, NotFoundError, PayloadTooLargeError, PreconditionFailedError, UnauthorizedError, UnprocessableEntityError, ValidationError, } from "../core/errors/http.ts";
53
+ export { BadRequestError, ConflictError, ForbiddenError, HttpError, isHttpErrorLike, NotFoundError, PayloadTooLargeError, PreconditionFailedError, toHttpError, UnauthorizedError, UnprocessableEntityError, ValidationError, } from "../core/errors/http.ts";
52
54
  export type { EventListener } from "../core/events/eventBus.ts";
53
55
  export { EventBus, eventBus } from "../core/events/eventBus.ts";
54
56
  export { modelEventName } from "../core/events/index.ts";
@@ -77,7 +79,7 @@ export { currentRequestMeta, runWithRequestMeta } from "../core/http/requestMeta
77
79
  export { createRequireAbilityMiddleware } from "../core/http/requireAbilityMiddleware.ts";
78
80
  export { createRequireGlobalAdminMiddleware } from "../core/http/requireGlobalAdminMiddleware.ts";
79
81
  export { createRequireWebAuthMiddleware } from "../core/http/requireWebAuthMiddleware.ts";
80
- export { serializeDate, toPaginatedResourceCollection, toResourceCollection, } from "../core/http/resources.ts";
82
+ export { JsonResource, ResourceCollection, serializeDate, toPaginatedResourceCollection, toResourceCollection, whenLoaded, } from "../core/http/resources.ts";
81
83
  export { createdResponse, jsonResponse, noContentResponse, withErrorHandling, } from "../core/http/response.ts";
82
84
  export type { RouteRequest } from "../core/http/route.ts";
83
85
  export { loginRedirectLocation, safeInternalRedirectPath, sanitizeInternalPath, } from "../core/http/safeInternalPath.ts";