@getstrata/core 2.2.3 → 2.2.5
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/database/whereBuilder.d.ts +7 -0
- package/dist/core/tenant/postgresAppRole.d.ts +3 -2
- package/dist/entries/database/query.js +6 -0
- package/dist/entries/database/repositoryQuery.js +6 -0
- package/dist/entries/tenant/enableTenantRls.js +3 -0
- package/dist/index.js +19 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.5
|
|
4
|
+
|
|
5
|
+
- fix(starter): keep container readiness independent of API admission
|
|
6
|
+
|
|
7
|
+
## 2.2.4
|
|
8
|
+
|
|
9
|
+
- perf(database): seek Postgres composite keyset bounds
|
|
10
|
+
|
|
3
11
|
## 2.2.3
|
|
4
12
|
|
|
5
13
|
- feat(database): add scoped composite keyset pagination
|
|
@@ -13,6 +13,13 @@ type WhereNode<TEntity extends object> = {
|
|
|
13
13
|
} | {
|
|
14
14
|
kind: "and" | "or";
|
|
15
15
|
exists: ExistsClause;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "and" | "or";
|
|
18
|
+
compareRow: {
|
|
19
|
+
columns: readonly string[];
|
|
20
|
+
operator: "lt" | "gt";
|
|
21
|
+
values: readonly unknown[];
|
|
22
|
+
};
|
|
16
23
|
};
|
|
17
24
|
declare class WhereBuilder<TEntity extends object, TWhere extends object = QueryWhere<TEntity>> {
|
|
18
25
|
readonly nodes: WhereNode<TEntity>[];
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* CREATE TABLE, so the NOBYPASSRLS runtime role cannot drop them. Runtime
|
|
7
7
|
* DATABASE_URL / APP_DATABASE_URL must be this NOBYPASSRLS role.
|
|
8
8
|
* docker-entrypoint-initdb.d only runs on an empty volume; call
|
|
9
|
-
* ensurePostgresDatabaseAndAppRole()
|
|
10
|
-
*
|
|
9
|
+
* ensurePostgresDatabaseAndAppRole() during explicit provisioning/migration so
|
|
10
|
+
* existing volumes get the role and table GRANTs after migrate:fresh. Production
|
|
11
|
+
* runtime processes only connect through the already-provisioned restricted role.
|
|
11
12
|
*
|
|
12
13
|
* Admin DDL (GRANT ALL TABLES, CREATE TABLE, DROP TABLE) must run on the
|
|
13
14
|
* application database. postgres/template1 are only for CREATE DATABASE.
|
|
@@ -149,6 +149,12 @@ function remapExistsSql(sql, existsParams, params) {
|
|
|
149
149
|
return sql.replace(/\$(\d+)/g, (_match, index) => `$${offset + Number(index)}`);
|
|
150
150
|
}
|
|
151
151
|
function buildWhereNodeClause(tableName, node, params) {
|
|
152
|
+
if ("compareRow" in node) {
|
|
153
|
+
const { columns, operator, values } = node.compareRow;
|
|
154
|
+
if (!Array.isArray(columns) || !columns.length || !Array.isArray(values) || columns.length !== values.length || !["lt", "gt"].includes(operator) || Array.from(columns).some((column) => typeof column !== "string" || !column.length))
|
|
155
|
+
throw new TypeError("Invalid row comparison columns, values or operator.");
|
|
156
|
+
return `(${columns.map((column) => qualifyColumn(tableName, column)).join(", ")}) ${operator === "lt" ? "<" : ">"} (${values.map((value) => pushParam(params, value)).join(", ")})`;
|
|
157
|
+
}
|
|
152
158
|
if ("exists" in node) {
|
|
153
159
|
const body = remapExistsSql(node.exists.sql, node.exists.params, params);
|
|
154
160
|
return `${node.exists.not ? "NOT " : ""}EXISTS (${body})`;
|
|
@@ -173,6 +173,12 @@ function remapExistsSql(sql, existsParams, params) {
|
|
|
173
173
|
return sql.replace(/\$(\d+)/g, (_match, index) => `$${offset + Number(index)}`);
|
|
174
174
|
}
|
|
175
175
|
function buildWhereNodeClause(tableName, node, params) {
|
|
176
|
+
if ("compareRow" in node) {
|
|
177
|
+
const { columns, operator, values } = node.compareRow;
|
|
178
|
+
if (!Array.isArray(columns) || !columns.length || !Array.isArray(values) || columns.length !== values.length || !["lt", "gt"].includes(operator) || Array.from(columns).some((column) => typeof column !== "string" || !column.length))
|
|
179
|
+
throw new TypeError("Invalid row comparison columns, values or operator.");
|
|
180
|
+
return `(${columns.map((column) => qualifyColumn(tableName, column)).join(", ")}) ${operator === "lt" ? "<" : ">"} (${values.map((value) => pushParam(params, value)).join(", ")})`;
|
|
181
|
+
}
|
|
176
182
|
if ("exists" in node) {
|
|
177
183
|
const body = remapExistsSql(node.exists.sql, node.exists.params, params);
|
|
178
184
|
return `${node.exists.not ? "NOT " : ""}EXISTS (${body})`;
|
|
@@ -109,6 +109,9 @@ function postgresAdminUrls(options) {
|
|
|
109
109
|
if (!isPostgresUrl(options.runtimeUrl)) {
|
|
110
110
|
return urls;
|
|
111
111
|
}
|
|
112
|
+
if (isProductionEnv() && !options.superuserPassword?.trim()) {
|
|
113
|
+
return urls;
|
|
114
|
+
}
|
|
112
115
|
const superuserPassword = options.superuserPassword ?? POSTGRES_SUPERUSER_PASSWORD;
|
|
113
116
|
push(withPostgresUrlCredentials(options.runtimeUrl, "postgres", superuserPassword));
|
|
114
117
|
if (!isProductionEnv()) {
|
package/dist/index.js
CHANGED
|
@@ -4132,6 +4132,7 @@ init_connectionContext();
|
|
|
4132
4132
|
init_dialect();
|
|
4133
4133
|
|
|
4134
4134
|
// ../../src/core/database/keyset.ts
|
|
4135
|
+
init_dialect();
|
|
4135
4136
|
var KEYSET_ALIAS_PREFIX = "__strata_keyset_";
|
|
4136
4137
|
function validateKeyset(table, options) {
|
|
4137
4138
|
if (!Number.isInteger(options.perPage) || options.perPage < 1 || options.perPage > 1000)
|
|
@@ -4159,6 +4160,18 @@ function validateKeyset(table, options) {
|
|
|
4159
4160
|
function keysetBoundary(order, cursor) {
|
|
4160
4161
|
if (!cursor)
|
|
4161
4162
|
return [];
|
|
4163
|
+
if (currentSqlDialect().driver === "pgsql" && order.length > 1 && order.every((item) => item.direction === order[0]?.direction)) {
|
|
4164
|
+
return [
|
|
4165
|
+
{
|
|
4166
|
+
kind: "and",
|
|
4167
|
+
compareRow: {
|
|
4168
|
+
columns: order.map((item) => item.column),
|
|
4169
|
+
operator: order[0]?.direction === "asc" ? "gt" : "lt",
|
|
4170
|
+
values: [...cursor.values]
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
];
|
|
4174
|
+
}
|
|
4162
4175
|
const branches = order.map((item, i) => {
|
|
4163
4176
|
const where = {};
|
|
4164
4177
|
order.slice(0, i).forEach((prefix, j) => {
|
|
@@ -4320,6 +4333,12 @@ function remapExistsSql(sql, existsParams, params) {
|
|
|
4320
4333
|
return sql.replace(/\$(\d+)/g, (_match, index) => `$${offset + Number(index)}`);
|
|
4321
4334
|
}
|
|
4322
4335
|
function buildWhereNodeClause(tableName, node, params) {
|
|
4336
|
+
if ("compareRow" in node) {
|
|
4337
|
+
const { columns, operator, values } = node.compareRow;
|
|
4338
|
+
if (!Array.isArray(columns) || !columns.length || !Array.isArray(values) || columns.length !== values.length || !["lt", "gt"].includes(operator) || Array.from(columns).some((column) => typeof column !== "string" || !column.length))
|
|
4339
|
+
throw new TypeError("Invalid row comparison columns, values or operator.");
|
|
4340
|
+
return `(${columns.map((column) => qualifyColumn(tableName, column)).join(", ")}) ${operator === "lt" ? "<" : ">"} (${values.map((value) => pushParam(params, value)).join(", ")})`;
|
|
4341
|
+
}
|
|
4323
4342
|
if ("exists" in node) {
|
|
4324
4343
|
const body = remapExistsSql(node.exists.sql, node.exists.params, params);
|
|
4325
4344
|
return `${node.exists.not ? "NOT " : ""}EXISTS (${body})`;
|