@stacksjs/database 0.70.44 → 0.70.53
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/LICENSE.md +21 -0
- package/dist/auth-tables.d.ts +56 -0
- package/dist/class-seeder.d.ts +49 -0
- package/dist/database.d.ts +3 -2
- package/dist/driver-config.d.ts +30 -1
- package/dist/drivers/dynamodb.d.ts +70 -18
- package/dist/drivers/helpers.d.ts +6 -0
- package/dist/drivers/mysql.d.ts +1 -1
- package/dist/drivers/postgres.d.ts +1 -1
- package/dist/drivers/sqlite.d.ts +6 -9
- package/dist/factory.d.ts +41 -0
- package/dist/fk-audit.d.ts +101 -0
- package/dist/index.d.ts +39 -2
- package/dist/index.js +1140 -1104
- package/dist/migration-lock.d.ts +23 -0
- package/dist/migrations.d.ts +46 -5
- package/dist/notification-tables.d.ts +20 -0
- package/dist/rbac-tables.d.ts +17 -0
- package/dist/seed-scaffold.d.ts +34 -0
- package/dist/seeder.d.ts +59 -0
- package/dist/sql-helpers.d.ts +2 -0
- package/dist/transaction-context.d.ts +52 -0
- package/dist/types.d.ts +82 -0
- package/dist/unique-audit.d.ts +60 -0
- package/dist/utils.d.ts +41 -2
- package/dist/uuid-columns.d.ts +22 -0
- package/package.json +11 -11
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Open Web Foundation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/auth-tables.d.ts
CHANGED
|
@@ -1,4 +1,60 @@
|
|
|
1
|
+
import { sqlHelpers } from './sql-helpers';
|
|
2
|
+
/**
|
|
3
|
+
* Defensive ALTER guaranteeing `users.email_verified_at` — the column
|
|
4
|
+
* `verifyEmail()` writes and the `verified` middleware reads, but which
|
|
5
|
+
* no generated users migration ever creates (stacksjs/stacks#1948).
|
|
6
|
+
* Pure builder so tests can assert per-dialect DDL without a live DB.
|
|
7
|
+
*/
|
|
8
|
+
export declare function usersEmailVerifiedAtSql(sql: SqlHelpers): string;
|
|
9
|
+
/**
|
|
10
|
+
* Defensive ALTER guaranteeing `users.password_changed_at` — the column
|
|
11
|
+
* `resetPassword()` stamps and the token-validation paths read to bind a
|
|
12
|
+
* token's validity to the user's credential state (stacksjs/stacks#1957,
|
|
13
|
+
* a #1947 follow-up). No generated users migration creates it, so the
|
|
14
|
+
* same pure-builder + try/catch-swallow pattern as
|
|
15
|
+
* {@link usersEmailVerifiedAtSql} guarantees it from both schema paths.
|
|
16
|
+
*/
|
|
17
|
+
export declare function usersPasswordChangedAtSql(sql: SqlHelpers): string;
|
|
18
|
+
/**
|
|
19
|
+
* Defensive ALTER guaranteeing `users.two_factor_secret` and
|
|
20
|
+
* `users.two_factor_enabled` — storage/framework/core/auth/src/
|
|
21
|
+
* authenticator.ts's TOTP helpers (generateTwoFactorSecret,
|
|
22
|
+
* verifyTwoFactorCode) have existed since early in the framework's
|
|
23
|
+
* history, but nothing ever created the columns a caller would persist
|
|
24
|
+
* them to, or the `passkeys`/`webauthn_challenges` tables
|
|
25
|
+
* storage/framework/core/auth/src/passkey.ts's WebAuthn helpers
|
|
26
|
+
* unconditionally query against — every passkey/2FA call site was
|
|
27
|
+
* dead code pointed at tables that never existed on any install,
|
|
28
|
+
* `buddy new` included. Same defensive ALTER + swallow pattern as
|
|
29
|
+
* {@link usersEmailVerifiedAtSql}.
|
|
30
|
+
*/
|
|
31
|
+
export declare function usersTwoFactorColumnsSql(sql: SqlHelpers): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Defensive ALTER guaranteeing `users.stripe_id` — the `billable`
|
|
34
|
+
* model trait's methods (createStripeCustomer/createOrGetStripeUser,
|
|
35
|
+
* used by `user.checkout(...)`) read and write this column
|
|
36
|
+
* unconditionally, but it's runtime-mixin-only (createBillableMethods
|
|
37
|
+
* in orm/define-model.ts, gated behind `traits.billable`): nothing in
|
|
38
|
+
* migration codegen ever creates the column itself, on any model, with
|
|
39
|
+
* or without the trait enabled. Every Stripe checkout call site was
|
|
40
|
+
* dead code pointed at a column that never existed, `buddy new`
|
|
41
|
+
* included — same shape as the passkeys/two_factor gap above (see
|
|
42
|
+
* stacksjs/status#1 Phase 9).
|
|
43
|
+
*/
|
|
44
|
+
export declare function usersStripeIdSql(): string;
|
|
45
|
+
/**
|
|
46
|
+
* Runs every `users` guarantee-column ALTER (email_verified_at,
|
|
47
|
+
* password_changed_at, two_factor_secret, two_factor_enabled,
|
|
48
|
+
* stripe_id), each independently try/catch-swallowed so one
|
|
49
|
+
* already-existing column (or a not-yet-existing `users` table) never
|
|
50
|
+
* skips the others. Exported so `buddy migrate`/`migrate:fresh` can
|
|
51
|
+
* call it a second time after the numbered model migrations run — see
|
|
52
|
+
* the call site in {@link migrateAuthTables} for why a single call
|
|
53
|
+
* isn't enough.
|
|
54
|
+
*/
|
|
55
|
+
export declare function ensureUsersAuthColumns(sql: SqlHelpers, options?: { verbose?: boolean }): Promise<void>;
|
|
1
56
|
/**
|
|
2
57
|
* Create all authentication tables
|
|
3
58
|
*/
|
|
4
59
|
export declare function migrateAuthTables(options?: { verbose?: boolean }): Promise<{ success: boolean, error?: string }>;
|
|
60
|
+
declare type SqlHelpers = ReturnType<typeof sqlHelpers>;
|
package/dist/class-seeder.d.ts
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topologically sort seeders by their declared `dependencies`. Ties
|
|
3
|
+
* (and dependency-free seeders) come out in alphabetical order so the
|
|
4
|
+
* result is deterministic across filesystems and runs.
|
|
5
|
+
*
|
|
6
|
+
* Unknown dependency names are dropped from the graph with a warning —
|
|
7
|
+
* they may refer to model-factory seeders that ran earlier in the
|
|
8
|
+
* `buddy seed` pipeline, or be stale references from a rename. The run
|
|
9
|
+
* doesn't fail because of them.
|
|
10
|
+
*
|
|
11
|
+
* Cycles throw with the offending class names in the error message.
|
|
12
|
+
*
|
|
13
|
+
* Exported for testing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function topoSortSeeders(seeders: Array<{ name: string, dependencies?: string[] }>): string[];
|
|
1
16
|
/*.ts`; an explicit `--class` filters to one.
|
|
17
|
+
*
|
|
18
|
+
* Ordering:
|
|
19
|
+
* 1. Files matching `*.ts` (excluding `_*.ts`) are imported.
|
|
20
|
+
* 2. If any seeder declares `dependencies`, the runnable set is
|
|
21
|
+
* topologically sorted (alphabetical tie-break).
|
|
22
|
+
* 3. Otherwise, the alphabetical order from `Array.sort()` wins —
|
|
23
|
+
* cheaper than the topo path and predictable across filesystems.
|
|
24
|
+
*
|
|
25
|
+
* Class-name filtering via `options.class` short-circuits both paths
|
|
26
|
+
* and runs only that one seeder. Cross-seeder dependencies are NOT
|
|
27
|
+
* resolved transitively in that mode — the caller takes responsibility
|
|
28
|
+
* for whatever prereqs are needed.
|
|
29
|
+
*
|
|
30
|
+
* See stacksjs/stacks#1855 for the original report of unsorted FS
|
|
31
|
+
* iteration producing zero-row seed runs.
|
|
2
32
|
*/
|
|
3
33
|
export declare function runClassSeeders(options?: RunOptions): Promise<{ ran: string[], skipped: string[] }>;
|
|
4
34
|
declare interface RunOptions {
|
|
@@ -9,8 +39,27 @@ declare interface RunOptions {
|
|
|
9
39
|
* Base class for class-based seeders. Subclass this and implement
|
|
10
40
|
* `async run()`. Seeders may call `this.call()` to invoke other
|
|
11
41
|
* seeders, mirroring Laravel's nested-seeder pattern.
|
|
42
|
+
*
|
|
43
|
+
* Cross-seeder ordering can be declared explicitly via `dependencies`:
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* export default class JudgeSeeder extends Seeder {
|
|
47
|
+
* dependencies = ['CourtHouseSeeder']
|
|
48
|
+
* async run() { ... }
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* The class name of each dependency is matched against the class names
|
|
53
|
+
* `runClassSeeders` discovered in the seeders directory. Unknown
|
|
54
|
+
* dependency names are warned about but don't fail the run (they may
|
|
55
|
+
* refer to a model-factory seeder run earlier in `buddy seed`).
|
|
56
|
+
*
|
|
57
|
+
* When no `dependencies` are declared, seeders run in alphabetical
|
|
58
|
+
* order — predictable across filesystems and good enough for projects
|
|
59
|
+
* that name seeders by data flow (CourtHouse → Judge → Review).
|
|
12
60
|
*/
|
|
13
61
|
export declare abstract class Seeder {
|
|
62
|
+
dependencies?: string[];
|
|
14
63
|
abstract run(): Promise<void> | void;
|
|
15
64
|
protected call(other: new () => Seeder): Promise<void>;
|
|
16
65
|
}
|
package/dist/database.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { QueryBuilder, QueryBuilderConfig, SupportedDialect } from '
|
|
1
|
+
import type { QueryBuilder, QueryBuilderConfig, SupportedDialect } from '@stacksjs/query-builder';
|
|
2
2
|
/**
|
|
3
3
|
* Create a database connection with the given options
|
|
4
4
|
*/
|
|
@@ -74,12 +74,13 @@ export declare class Database {
|
|
|
74
74
|
get query(): QueryBuilder<any>;
|
|
75
75
|
initialize(): void;
|
|
76
76
|
switchDriver(driver: SupportedDialect, connection: DatabaseConnectionConfig): void;
|
|
77
|
-
close(): void
|
|
77
|
+
close(): Promise<void>;
|
|
78
78
|
static fromConfig(config: {
|
|
79
79
|
default: SupportedDialect
|
|
80
80
|
connections: {
|
|
81
81
|
sqlite?: { database: string }
|
|
82
82
|
mysql?: { name: string, host?: string, port?: number, username?: string, password?: string }
|
|
83
|
+
singlestore?: { name: string, host?: string, port?: number, username?: string, password?: string }
|
|
83
84
|
postgres?: { name: string, host?: string, port?: number, username?: string, password?: string }
|
|
84
85
|
}
|
|
85
86
|
}, env?: string): Database;
|
package/dist/driver-config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SupportedDialect } from '
|
|
1
|
+
import type { SupportedDialect } from '@stacksjs/query-builder';
|
|
2
2
|
/**
|
|
3
3
|
* Get the connection string for a given driver and configuration
|
|
4
4
|
*/
|
|
@@ -36,6 +36,16 @@ export declare const driverDefaults: {
|
|
|
36
36
|
prefix: '';
|
|
37
37
|
charset: 'utf8mb4';
|
|
38
38
|
collation: 'utf8mb4_unicode_ci'
|
|
39
|
+
};
|
|
40
|
+
singlestore: {
|
|
41
|
+
name: 'stacks';
|
|
42
|
+
host: '127.0.0.1';
|
|
43
|
+
port: 3306;
|
|
44
|
+
username: 'root';
|
|
45
|
+
password: '';
|
|
46
|
+
prefix: '';
|
|
47
|
+
charset: 'utf8mb4';
|
|
48
|
+
ssl: false
|
|
39
49
|
};
|
|
40
50
|
postgres: {
|
|
41
51
|
name: 'stacks';
|
|
@@ -68,6 +78,24 @@ export declare interface MysqlConfig {
|
|
|
68
78
|
charset?: string
|
|
69
79
|
collation?: string
|
|
70
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* SingleStore specific configuration.
|
|
83
|
+
*
|
|
84
|
+
* SingleStore (formerly MemSQL) speaks the MySQL wire protocol, so it shares
|
|
85
|
+
* MySQL's connection shape. It diverges only in DDL (distributed tables with
|
|
86
|
+
* SHARD KEY / SORT KEY, no foreign keys) — handled by the migration generator,
|
|
87
|
+
* not by the connection layer.
|
|
88
|
+
*/
|
|
89
|
+
export declare interface SinglestoreConfig {
|
|
90
|
+
name: string
|
|
91
|
+
host?: string
|
|
92
|
+
port?: number
|
|
93
|
+
username?: string
|
|
94
|
+
password?: string
|
|
95
|
+
prefix?: string
|
|
96
|
+
charset?: string
|
|
97
|
+
ssl?: boolean
|
|
98
|
+
}
|
|
71
99
|
/**
|
|
72
100
|
* PostgreSQL specific configuration
|
|
73
101
|
*/
|
|
@@ -106,6 +134,7 @@ export declare interface DynamoDbConfig {
|
|
|
106
134
|
export declare interface DatabaseConnections {
|
|
107
135
|
sqlite?: SqliteConfig
|
|
108
136
|
mysql?: MysqlConfig
|
|
137
|
+
singlestore?: SinglestoreConfig
|
|
109
138
|
postgres?: PostgresConfig
|
|
110
139
|
dynamodb?: DynamoDbConfig
|
|
111
140
|
}
|
|
@@ -2,11 +2,11 @@ import type { Model } from '@stacksjs/types';
|
|
|
2
2
|
/**
|
|
3
3
|
* Marshall a JS object to DynamoDB format
|
|
4
4
|
*/
|
|
5
|
-
declare function marshall(obj: Record<string,
|
|
5
|
+
declare function marshall(obj: Record<string, unknown>): DynamoItem;
|
|
6
6
|
/**
|
|
7
7
|
* Unmarshall a DynamoDB object to JS format
|
|
8
8
|
*/
|
|
9
|
-
declare function unmarshall(obj:
|
|
9
|
+
declare function unmarshall(obj: DynamoItem): Record<string, unknown>;
|
|
10
10
|
/**
|
|
11
11
|
* Generate key pattern for an entity
|
|
12
12
|
*/
|
|
@@ -27,6 +27,32 @@ export declare function createDynamo(): DynamoClient;
|
|
|
27
27
|
* DynamoDB client singleton
|
|
28
28
|
*/
|
|
29
29
|
export declare const dynamo: DynamoClient;
|
|
30
|
+
/**
|
|
31
|
+
* Minimal AWS-SDK `DynamoDBClient` shape — just the methods this
|
|
32
|
+
* driver calls. Typed locally so callers wiring their own SDK
|
|
33
|
+
* client into `setClient()` get a clear compile-time contract
|
|
34
|
+
* instead of a runtime "function not found" surprise.
|
|
35
|
+
*/
|
|
36
|
+
export declare interface DynamoSdkClient {
|
|
37
|
+
query: (req: Record<string, unknown>) => Promise<{
|
|
38
|
+
Items?: DynamoItem[]
|
|
39
|
+
Count?: number
|
|
40
|
+
ScannedCount?: number
|
|
41
|
+
LastEvaluatedKey?: DynamoItem
|
|
42
|
+
}>
|
|
43
|
+
scan: (req: Record<string, unknown>) => Promise<{
|
|
44
|
+
Items?: DynamoItem[]
|
|
45
|
+
Count?: number
|
|
46
|
+
ScannedCount?: number
|
|
47
|
+
LastEvaluatedKey?: DynamoItem
|
|
48
|
+
}>
|
|
49
|
+
getItem: (req: Record<string, unknown>) => Promise<{ Item?: DynamoItem }>
|
|
50
|
+
putItem: (req: Record<string, unknown>) => Promise<unknown>
|
|
51
|
+
deleteItem: (req: Record<string, unknown>) => Promise<unknown>
|
|
52
|
+
updateItem: (req: Record<string, unknown>) => Promise<unknown>
|
|
53
|
+
batchWriteItem: (req: Record<string, unknown>) => Promise<unknown>
|
|
54
|
+
transactWriteItems: (req: Record<string, unknown>) => Promise<unknown>
|
|
55
|
+
}
|
|
30
56
|
/**
|
|
31
57
|
* DynamoDB connection configuration
|
|
32
58
|
*/
|
|
@@ -72,46 +98,72 @@ export declare interface SortKeyBuilder {
|
|
|
72
98
|
* Batch write operation
|
|
73
99
|
*/
|
|
74
100
|
export declare interface BatchWriteOperation {
|
|
75
|
-
put?: { entity: string, item: Record<string,
|
|
101
|
+
put?: { entity: string, item: Record<string, unknown> }
|
|
76
102
|
delete?: { entity: string, pk: string, sk: string }
|
|
77
103
|
}
|
|
78
104
|
/**
|
|
79
105
|
* Transact write operation
|
|
80
106
|
*/
|
|
81
107
|
export declare interface TransactWriteOperation {
|
|
82
|
-
put?: { entity: string, item: Record<string,
|
|
83
|
-
update?: { entity: string, pk: string, sk?: string, set?: Record<string,
|
|
108
|
+
put?: { entity: string, item: Record<string, unknown>, condition?: string }
|
|
109
|
+
update?: { entity: string, pk: string, sk?: string, set?: Record<string, unknown>, add?: Record<string, number>, remove?: string[] }
|
|
84
110
|
delete?: { entity: string, pk: string, sk: string, condition?: string }
|
|
85
111
|
conditionCheck?: { entity: string, pk: string, sk: string, condition: string }
|
|
86
112
|
}
|
|
87
113
|
/**
|
|
88
114
|
* Query result
|
|
89
115
|
*/
|
|
90
|
-
export declare interface QueryResult<T =
|
|
116
|
+
export declare interface QueryResult<T = unknown> {
|
|
91
117
|
items: T[]
|
|
92
118
|
count: number
|
|
93
119
|
scannedCount?: number
|
|
94
|
-
lastKey?: Record<string,
|
|
120
|
+
lastKey?: Record<string, unknown>
|
|
95
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* DynamoDB AttributeValue discriminated union (stacksjs/stacks#1894 T-8).
|
|
124
|
+
*
|
|
125
|
+
* Mirrors the wire format AWS SDK's `DynamoDBClient` produces and
|
|
126
|
+
* consumes, declared locally so this package doesn't take a hard
|
|
127
|
+
* dependency on `@aws-sdk/client-dynamodb` just for its types. Each
|
|
128
|
+
* concrete variant carries its own typed payload; consumers narrow
|
|
129
|
+
* via `'S' in attr` / etc.
|
|
130
|
+
*
|
|
131
|
+
* Why not just import from AWS SDK: the framework's DynamoDB driver
|
|
132
|
+
* ships as an optional peer integration. Pulling AWS SDK as a type-
|
|
133
|
+
* level dep would force every Stacks app to install it whether or
|
|
134
|
+
* not they use the driver.
|
|
135
|
+
*/
|
|
136
|
+
export type DynamoAttributeValue = | { S: string }
|
|
137
|
+
| { N: string }
|
|
138
|
+
| { B: Uint8Array | string }
|
|
139
|
+
| { BOOL: boolean }
|
|
140
|
+
| { NULL: true }
|
|
141
|
+
| { L: DynamoAttributeValue[] }
|
|
142
|
+
| { M: Record<string, DynamoAttributeValue> }
|
|
143
|
+
| { SS: string[] }
|
|
144
|
+
| { NS: string[] }
|
|
145
|
+
| { BS: Array<Uint8Array | string> }
|
|
146
|
+
/** Marshalled item — the on-the-wire representation. */
|
|
147
|
+
export type DynamoItem = Record<string, DynamoAttributeValue>;
|
|
96
148
|
/**
|
|
97
149
|
* Entity-centric query builder for DynamoDB
|
|
98
150
|
*/
|
|
99
|
-
export declare class EntityQueryBuilder<T =
|
|
100
|
-
constructor(client:
|
|
151
|
+
export declare class EntityQueryBuilder<T = unknown> {
|
|
152
|
+
constructor(client: DynamoSdkClient | undefined, tableName: string, config: { pkAttribute: string, skAttribute: string, entityTypeAttribute: string, keyDelimiter: string });
|
|
101
153
|
entity(entityType: string): this;
|
|
102
154
|
pk(value: string): this;
|
|
103
155
|
get sk(): SortKeyBuilder;
|
|
104
156
|
index(indexName: string): this;
|
|
105
157
|
project(...attributes: string[]): this;
|
|
106
|
-
filter(attribute: string, operator: string, value?:
|
|
107
|
-
where(attribute: string, value:
|
|
108
|
-
whereIn(attribute: string, values:
|
|
158
|
+
filter(attribute: string, operator: string, value?: unknown): this;
|
|
159
|
+
where(attribute: string, value: unknown): this;
|
|
160
|
+
whereIn(attribute: string, values: unknown[]): this;
|
|
109
161
|
limit(count: number): this;
|
|
110
162
|
asc(): this;
|
|
111
163
|
desc(): this;
|
|
112
164
|
consistent(): this;
|
|
113
|
-
startFrom(key: Record<string,
|
|
114
|
-
toRequest(): Record<string,
|
|
165
|
+
startFrom(key: Record<string, unknown>): this;
|
|
166
|
+
toRequest(): Record<string, unknown>;
|
|
115
167
|
get(): Promise<T[]>;
|
|
116
168
|
first(): Promise<T | undefined>;
|
|
117
169
|
getAll(): Promise<T[]>;
|
|
@@ -123,18 +175,18 @@ export declare class EntityQueryBuilder<T = any> {
|
|
|
123
175
|
declare class DynamoClient {
|
|
124
176
|
connection(config: DynamoConnectionConfig): this;
|
|
125
177
|
isConfigured(): boolean;
|
|
126
|
-
setClient(client:
|
|
127
|
-
getClient():
|
|
178
|
+
setClient(client: DynamoSdkClient): this;
|
|
179
|
+
getClient(): DynamoSdkClient | undefined;
|
|
128
180
|
registerEntity(mapping: SingleTableEntityMapping): this;
|
|
129
181
|
registerModel(model: Model): this;
|
|
130
182
|
getEntityMapping(entityType: string): SingleTableEntityMapping | undefined;
|
|
131
183
|
entity<T = any>(entityType: string): EntityQueryBuilder<T>;
|
|
132
184
|
batchWrite(operations: BatchWriteOperation[]): Promise<void>;
|
|
133
185
|
transactWrite(operations: TransactWriteOperation[]): Promise<void>;
|
|
134
|
-
put(entity: string, item: Record<string,
|
|
186
|
+
put(entity: string, item: Record<string, unknown>): Promise<void>;
|
|
135
187
|
get<T = any>(pk: string, sk?: string): Promise<T | undefined>;
|
|
136
188
|
delete(pk: string, sk?: string): Promise<void>;
|
|
137
|
-
update(pk: string, sk: string | undefined, updates: Record<string,
|
|
189
|
+
update(pk: string, sk: string | undefined, updates: Record<string, unknown>): Promise<void>;
|
|
138
190
|
getTableName(): string;
|
|
139
191
|
getConfig(): {
|
|
140
192
|
tableName: string
|
|
@@ -18,6 +18,12 @@ export declare function isArrayEqual(arr1: (number | undefined)[], arr2: (number
|
|
|
18
18
|
export declare function findDifferingKeys(obj1: any, obj2: any): { key: string, max: number, min: number }[];
|
|
19
19
|
export declare function fetchTables(): Promise<string[]>;
|
|
20
20
|
export declare function getUpvoteTableName(model: Model, tableName: string): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Foreign-key column of the likes pivot. MUST mirror the runtime default in
|
|
23
|
+
* orm/src/traits/likeable.ts (`${tableName.replace(/s$/, '')}_id`) — a pivot
|
|
24
|
+
* generated with any other column name is a table `like()` cannot write to.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getLikeableForeignKey(model: Model, tableName: string): string;
|
|
21
27
|
export declare function prepareNumberColumnType(validator: NumberValidatorType, driver?: string): string;
|
|
22
28
|
// Add new function for enum column types
|
|
23
29
|
export declare function prepareEnumColumnType(validator: EnumValidatorType, driver?: string): string;
|
package/dist/drivers/mysql.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export declare function resetMysqlDatabase(): Promise<Ok<string, never>>;
|
|
|
3
3
|
export declare function dropMysqlTables(): Promise<void>;
|
|
4
4
|
export declare function generateMysqlMigration(modelPath: string): Promise<void>;
|
|
5
5
|
export declare function generateMysqlTraitMigrations(): Promise<void>;
|
|
6
|
-
export declare function createMysqlForeignKeyMigrations(modelPath: string): Promise<void>;
|
|
7
6
|
export declare function createAlterTableMigration(modelPath: string): Promise<void>;
|
|
7
|
+
export declare function generateIndexCreationSQL(tableName: string, index: { name: string, columns: string[], unique?: boolean, where?: string }): string;
|
|
@@ -3,5 +3,5 @@ export declare function dropPostgresTables(): Promise<void>;
|
|
|
3
3
|
export declare function generatePostgresTraitMigrations(): Promise<void>;
|
|
4
4
|
export declare function resetPostgresDatabase(): Promise<Ok<string, never>>;
|
|
5
5
|
export declare function generatePostgresMigration(modelPath: string): Promise<void>;
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function generateIndexCreationSQL(tableName: string, index: { name: string, columns: string[], unique?: boolean, where?: string }): string;
|
|
7
7
|
export declare function fetchPostgresTables(): Promise<string[]>;
|
package/dist/drivers/sqlite.d.ts
CHANGED
|
@@ -5,19 +5,16 @@ export declare function resetSqliteDatabase(): Promise<Ok<string, never>>;
|
|
|
5
5
|
* cheap to re-apply, but each one is a no-op once set so repeated calls
|
|
6
6
|
* during dev hot-reload don't accumulate state.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* relations land in the DB and fail later, far from the original write.
|
|
14
|
-
* - busy_timeout: backs off when the file is locked by another writer
|
|
15
|
-
* instead of failing the whole query immediately.
|
|
8
|
+
* Connection bootstrap now applies SQLITE_BOOTSTRAP_PRAGMAS automatically
|
|
9
|
+
* inside @stacksjs/query-builder's wrapped `createQueryBuilder`
|
|
10
|
+
* (stacksjs/stacks#1951); this export remains for explicit re-application.
|
|
11
|
+
* See the shared list in @stacksjs/query-builder for what each pragma does
|
|
12
|
+
* and why it must be set per connection.
|
|
16
13
|
*/
|
|
17
14
|
export declare function configureSqlitePragmas(): Promise<void>;
|
|
18
15
|
export declare function dropSqliteTables(): Promise<void>;
|
|
19
16
|
export declare function fetchSqliteFile(): string;
|
|
20
17
|
export declare function fetchTestSqliteFile(): string;
|
|
21
18
|
export declare function generateSqliteMigration(modelPath: string): Promise<void>;
|
|
22
|
-
export declare function createSqliteForeignKeyMigrations(_modelPath: string): Promise<void>;
|
|
23
19
|
export declare function copyModelFiles(modelPath: string): Promise<void>;
|
|
20
|
+
export declare function generateIndexCreationSQL(tableName: string, index: { name: string, columns: string[], unique?: boolean, where?: string }): string;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Attribute, Model } from '@stacksjs/types';
|
|
2
|
+
import type { SeedResult } from './seeder';
|
|
3
|
+
/**
|
|
4
|
+
* Build the internal `SeederModel`-shaped payload that `seedModelDirect`
|
|
5
|
+
* expects from a public-API call. Pure function — exported separately
|
|
6
|
+
* so tests can assert the override-precedence rules without touching
|
|
7
|
+
* the database.
|
|
8
|
+
*
|
|
9
|
+
* Precedence (lowest → highest): per-attribute `factory` output →
|
|
10
|
+
* global `options.with` → per-row `options.rows[i]`. All keys are
|
|
11
|
+
* snake-cased before insert so callers can use the model's camelCase
|
|
12
|
+
* attribute names without thinking about column naming.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildSeederPayload(modelInput: unknown, options?: GenerateOptions): {
|
|
15
|
+
name: string
|
|
16
|
+
table: string
|
|
17
|
+
count: number
|
|
18
|
+
fixtures: Array<Record<string, unknown>>
|
|
19
|
+
attributes: Record<string, Attribute>
|
|
20
|
+
model: Model
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Generate factory rows for a model and insert them. Designed to be
|
|
24
|
+
* called from a class seeder.
|
|
25
|
+
*
|
|
26
|
+
* Honours the model's per-attribute `factory: faker => …` declarations
|
|
27
|
+
* — exactly the same code path as the legacy auto-walker — but without
|
|
28
|
+
* the implicit "every model with `useSeeder` fires on every run"
|
|
29
|
+
* coupling. See stacksjs/stacks#1919 for the rationale.
|
|
30
|
+
*/
|
|
31
|
+
export declare function generate(modelInput: unknown, options?: GenerateOptions): Promise<SeedResult>;
|
|
32
|
+
export declare const factory: {
|
|
33
|
+
|
|
34
|
+
};
|
|
35
|
+
export declare interface GenerateOptions {
|
|
36
|
+
count?: number
|
|
37
|
+
fresh?: boolean
|
|
38
|
+
verbose?: boolean
|
|
39
|
+
with?: Record<string, unknown>
|
|
40
|
+
rows?: Array<Record<string, unknown>>
|
|
41
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glob, but resilient to "directory doesn't exist yet" (the userland
|
|
3
|
+
* `app/Models/` directory in particular often doesn't exist when the
|
|
4
|
+
* audit runs in framework tests or scaffolded apps that haven't
|
|
5
|
+
* created any models yet). Avoids the Bun Glob ENOENT.
|
|
6
|
+
*/
|
|
7
|
+
export declare function safeGlob(pattern: string): string[];
|
|
8
|
+
/**
|
|
9
|
+
* Walk every model file (user + framework defaults) and return the
|
|
10
|
+
* full list of declared `belongsTo` foreign keys.
|
|
11
|
+
*
|
|
12
|
+
* Convention: `Comment` with `belongsTo: ['Post']` implies the FK
|
|
13
|
+
* `comments.post_id → posts.id`. Same shape the migration generator
|
|
14
|
+
* uses, so we keep it consistent here. Other relationship types
|
|
15
|
+
* (`hasOne`, `hasMany`, `belongsToMany` through tables) imply FKs in
|
|
16
|
+
* the *other* direction or in pivot tables — we only check
|
|
17
|
+
* `belongsTo` here for the simplest, highest-signal audit.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getDeclaredFKs(): Promise<DeclaredFK[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Query the live database for every foreign key constraint, returning
|
|
22
|
+
* a normalised shape. Dialect-aware: PRAGMA on SQLite,
|
|
23
|
+
* information_schema on MySQL / PostgreSQL.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getLiveFKs(): Promise<LiveFK[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Diff declared FKs against live FKs. Returns the declared FKs that
|
|
28
|
+
* have no matching row in the live database — these are the silent
|
|
29
|
+
* "FK should be enforcing referential integrity but isn't" cases that
|
|
30
|
+
* motivated this audit.
|
|
31
|
+
*
|
|
32
|
+
* Match key is `fromTable.fromColumn → toTable.toColumn` (case-
|
|
33
|
+
* insensitive). Extra live FKs (present in DB but not in any model)
|
|
34
|
+
* are not reported — those usually come from manual migrations or
|
|
35
|
+
* external tooling, both legitimate.
|
|
36
|
+
*/
|
|
37
|
+
export declare function auditForeignKeys(): Promise<FkAuditResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Scan the live database for rows whose foreign key references a
|
|
40
|
+
* parent row that doesn't exist. SQLite's `PRAGMA foreign_key_check`
|
|
41
|
+
* does this regardless of the `foreign_keys` pragma state and excludes
|
|
42
|
+
* NULL-FK rows. The violating column is resolved by joining the
|
|
43
|
+
* reported `fkid` against `PRAGMA foreign_key_list(table)`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function findFkOrphans(dialect?: 'sqlite' | 'mysql' | 'postgres' | 'other'): Promise<FkOrphanReport>;
|
|
46
|
+
// stacksjs/stacks#1916 — Foreign-key audit. Compares each model's
|
|
47
|
+
// declared `belongsTo` relationships against the FKs that actually
|
|
48
|
+
// exist in the live database. Drives `buddy doctor`'s FK integrity
|
|
49
|
+
// check, surfaces the "you flipped DB_CONNECTION but the FKs didn't
|
|
50
|
+
// follow" failure mode that motivated #1915 and #1916.
|
|
51
|
+
//
|
|
52
|
+
// Two halves:
|
|
53
|
+
//
|
|
54
|
+
// 1. Declared FKs: walk model files, look at `belongsTo`, compute
|
|
55
|
+
// the implied FK shape `{ fromTable, fromColumn, toTable,
|
|
56
|
+
// toColumn }`. Convention is `<related>_id` → `<related>.id`,
|
|
57
|
+
// same as the migration generator.
|
|
58
|
+
//
|
|
59
|
+
// 2. Live FKs: query the live database. SQLite via
|
|
60
|
+
// `PRAGMA foreign_key_list("…")`, MySQL/Postgres via
|
|
61
|
+
// `information_schema.key_column_usage`.
|
|
62
|
+
export declare interface DeclaredFK {
|
|
63
|
+
fromTable: string
|
|
64
|
+
fromColumn: string
|
|
65
|
+
toTable: string
|
|
66
|
+
toColumn: string
|
|
67
|
+
model: string
|
|
68
|
+
}
|
|
69
|
+
export declare interface LiveFK {
|
|
70
|
+
fromTable: string
|
|
71
|
+
fromColumn: string
|
|
72
|
+
toTable: string
|
|
73
|
+
toColumn: string
|
|
74
|
+
}
|
|
75
|
+
export declare interface FkAuditResult {
|
|
76
|
+
declared: DeclaredFK[]
|
|
77
|
+
live: LiveFK[]
|
|
78
|
+
missing: DeclaredFK[]
|
|
79
|
+
}
|
|
80
|
+
// stacksjs/stacks#1951 — FK orphan detection. FK enforcement flipped
|
|
81
|
+
// ON (utils.ts bootstrap pragmas) against databases that were written
|
|
82
|
+
// while `foreign_keys = OFF`, so legacy rows can reference parents that
|
|
83
|
+
// no longer exist. Those orphans silently turn previously-working
|
|
84
|
+
// deletes/inserts into runtime FK failures. This READ-ONLY scan finds
|
|
85
|
+
// them so `buddy doctor` can report before they bite.
|
|
86
|
+
//
|
|
87
|
+
// SQLite-specific: MySQL/Postgres enforce FKs natively, so the
|
|
88
|
+
// FK-off-legacy-data failure mode can't arise there — we degrade to
|
|
89
|
+
// `supported: false` rather than run an expensive per-FK anti-join.
|
|
90
|
+
export declare interface FkOrphan {
|
|
91
|
+
table: string
|
|
92
|
+
column: string
|
|
93
|
+
parent: string
|
|
94
|
+
count: number
|
|
95
|
+
sampleRowids: number[]
|
|
96
|
+
}
|
|
97
|
+
export declare interface FkOrphanReport {
|
|
98
|
+
supported: boolean
|
|
99
|
+
total: number
|
|
100
|
+
orphans: FkOrphan[]
|
|
101
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,12 +10,16 @@ export type {
|
|
|
10
10
|
PostgresConfig,
|
|
11
11
|
SqliteConfig,
|
|
12
12
|
} from './driver-config';
|
|
13
|
+
export type { GenerateOptions } from './factory';
|
|
14
|
+
export type { ScaffoldOptions, ScaffoldResult } from './seed-scaffold';
|
|
15
|
+
export type { DeclaredFK, FkAuditResult, FkOrphan, FkOrphanReport, LiveFK } from './fk-audit';
|
|
16
|
+
export type { DeclaredUnique, LiveUniqueIndex, UniqueAuditResult } from './unique-audit';
|
|
13
17
|
export type {
|
|
14
18
|
QueryBuilder,
|
|
15
19
|
QueryBuilderConfig,
|
|
16
20
|
Seeder as QueryBuilderSeeder,
|
|
17
21
|
SupportedDialect,
|
|
18
|
-
} from '
|
|
22
|
+
} from '@stacksjs/query-builder';
|
|
19
23
|
export type {
|
|
20
24
|
DynamoConnectionConfig,
|
|
21
25
|
SingleTableEntityMapping,
|
|
@@ -85,20 +89,53 @@ export { Seeder, runClassSeeders } from './class-seeder';
|
|
|
85
89
|
export { addColumnSafely, backfillInBatches, renameColumnSafely } from './safe-migrations';
|
|
86
90
|
// Seeding
|
|
87
91
|
export * from './seeder';
|
|
92
|
+
// stacksjs/stacks#1919 — public factory API. The canonical replacement
|
|
93
|
+
// for the legacy `useSeeder` trait + auto-walker. Class seeders call
|
|
94
|
+
// `factory.generate(Model, opts)` explicitly so there's one
|
|
95
|
+
// orchestration layer per table, no double-fire on tables that have
|
|
96
|
+
// both a `useSeeder` trait and a class seeder file.
|
|
97
|
+
export { factory, generate as factoryGenerate } from './factory';
|
|
98
|
+
// `buddy seed:scaffold` codemod — generates class-seeder files for
|
|
99
|
+
// every model with a `useSeeder` trait, easing the migration off the
|
|
100
|
+
// auto-walker.
|
|
101
|
+
export { scaffoldClassSeedersFromModels, renderSeederFile } from './seed-scaffold';
|
|
88
102
|
// Driver utilities
|
|
89
103
|
export * from './drivers/index';
|
|
90
104
|
// Custom migrations (jobs, errors, etc.)
|
|
91
105
|
export * from './custom/index';
|
|
92
106
|
// Auth tables migration
|
|
93
107
|
export * from './auth-tables';
|
|
108
|
+
// uuid column guarantee for `useUuid` models (stacksjs/status#1 Phase 9)
|
|
109
|
+
export * from './uuid-columns';
|
|
110
|
+
// Notification tables migration (stacksjs/stacks#1937)
|
|
111
|
+
export { migrateNotificationTables } from './notification-tables';
|
|
112
|
+
// RBAC tables migration (stacksjs/stacks#1941 Phase A)
|
|
113
|
+
export { migrateRbacTables } from './rbac-tables';
|
|
94
114
|
// SQL dialect helpers & connection defaults
|
|
95
115
|
export * from './sql-helpers';
|
|
96
116
|
export * from './defaults';
|
|
117
|
+
// Foreign-key audit (stacksjs/stacks#1916) — compare declared
|
|
118
|
+
// `belongsTo` relationships against live FKs.
|
|
119
|
+
export { auditForeignKeys, findFkOrphans, getDeclaredFKs, getLiveFKs } from './fk-audit';
|
|
120
|
+
// Unique-index drift audit (stacksjs/stacks#1952) — compare declared
|
|
121
|
+
// `unique: true` attributes / indexes against live UNIQUE indexes.
|
|
122
|
+
export { auditUniqueIndexes, getDeclaredUniques, getLiveUniqueIndexes } from './unique-audit';
|
|
123
|
+
// Transaction context: AsyncLocalStorage-based scope so side-effect
|
|
124
|
+
// emitters (queue dispatch, mailer send) can buffer themselves
|
|
125
|
+
// until the surrounding `db.transaction(...)` commits
|
|
126
|
+
// (stacksjs/stacks#1882).
|
|
127
|
+
export {
|
|
128
|
+
__flushAfterCommitNow,
|
|
129
|
+
__pendingAfterCommitCount,
|
|
130
|
+
enqueueAfterCommit,
|
|
131
|
+
isInTransaction,
|
|
132
|
+
runInTransactionScope,
|
|
133
|
+
} from './transaction-context';
|
|
97
134
|
// Re-export bun-query-builder functions and types
|
|
98
135
|
export {
|
|
99
136
|
createQueryBuilder,
|
|
100
137
|
setConfig,
|
|
101
|
-
} from '
|
|
138
|
+
} from '@stacksjs/query-builder';
|
|
102
139
|
// DynamoDB entity-centric API
|
|
103
140
|
export {
|
|
104
141
|
createDynamo,
|