@drhomet/mithaq 0.1.1 → 0.2.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/LICENSES/openstatus-time-picker.txt +21 -0
- package/README.md +161 -0
- package/dist/asas/build.d.ts +10 -2
- package/dist/asas/build.js +69 -22
- package/dist/asas/cli/mithaq.js +35 -3
- package/dist/asas/cli/upgrade.d.ts +22 -9
- package/dist/asas/cli/upgrade.js +187 -78
- package/dist/asas/http/fetch.d.ts +21 -3
- package/dist/asas/http/fetch.js +123 -60
- package/dist/asas/http/orpc.d.ts +5 -3
- package/dist/asas/http/orpc.js +126 -108
- package/dist/asas/runner/bulk.d.ts +1 -1
- package/dist/asas/runner/bulk.js +16 -11
- package/dist/asas/runner/errors.d.ts +5 -2
- package/dist/asas/runner/errors.js +18 -2
- package/dist/asas/runner/read.d.ts +6 -2
- package/dist/asas/runner/read.js +28 -6
- package/dist/asas/runner/steps.d.ts +22 -4
- package/dist/asas/runner/steps.js +15 -9
- package/dist/asas/runner/store.d.ts +4 -0
- package/dist/asas/runner/store.js +21 -0
- package/dist/asas/runner/tenant.d.ts +2 -2
- package/dist/asas/runner/tenant.js +78 -16
- package/dist/asas/runner/write.d.ts +173 -11
- package/dist/asas/runner/write.js +274 -46
- package/dist/auth/better-auth/access.d.ts +14 -0
- package/dist/auth/better-auth/access.js +32 -0
- package/dist/auth/better-auth/generate.d.ts +3 -1
- package/dist/auth/better-auth/generate.js +26 -8
- package/dist/auth/better-auth/index.d.ts +2 -0
- package/dist/auth/better-auth/tenant.d.ts +11 -3
- package/dist/auth/better-auth/tenant.js +10 -2
- package/dist/auth/local/index.js +5 -3
- package/dist/base/fields.d.ts +3 -2
- package/dist/base/fields.js +3 -2
- package/dist/base/index.d.ts +2 -1
- package/dist/base/json-schema.d.ts +7 -0
- package/dist/base/json-schema.js +35 -0
- package/dist/base/kinds.d.ts +21 -0
- package/dist/base/kinds.js +46 -1
- package/dist/base/rules.d.ts +4 -4
- package/dist/base/rules.js +4 -3
- package/dist/drizzle/index.d.ts +9 -139
- package/dist/drizzle/index.js +143 -24
- package/dist/drizzle/kinds.d.ts +1 -0
- package/dist/drizzle/kinds.js +3 -1
- package/dist/drizzle/migrate.d.ts +26 -0
- package/dist/drizzle/migrate.js +69 -26
- package/dist/drizzle/rules.d.ts +1 -1
- package/dist/index.d.ts +11 -21
- package/dist/index.js +8 -18
- package/dist/mithaq/define.d.ts +62 -15
- package/dist/mithaq/define.js +23 -9
- package/dist/mithaq/generate-messages.js +5 -4
- package/dist/mithaq/language.d.ts +43 -4
- package/dist/mithaq/message-file.d.ts +13 -0
- package/dist/mithaq/message-file.js +29 -0
- package/dist/mithaq/messages.js +5 -0
- package/dist/mithaq/resolve.js +70 -10
- package/dist/mithaq/types.d.ts +29 -1
- package/dist/rbac/index.d.ts +2 -2
- package/dist/rbac/index.js +1 -1
- package/dist/rich-text/index.d.ts +1 -0
- package/dist/rich-text/index.js +1 -0
- package/dist/rich-text/rich-text.d.ts +2 -55
- package/dist/rich-text/rich-text.js +2 -125
- package/dist/rich-text/value.d.ts +55 -0
- package/dist/rich-text/value.js +126 -0
- package/dist/screen/client.d.ts +5 -2
- package/dist/screen/client.js +5 -2
- package/dist/screen/generate.js +48 -6
- package/dist/screen/index.d.ts +18 -1
- package/dist/screen/index.js +24 -6
- package/dist/screen/react.d.ts +2 -0
- package/dist/storage/index.d.ts +2 -3
- package/dist/storage/index.js +1 -2
- package/dist/storage/storage.d.ts +2 -0
- package/dist/storage/storage.js +9 -2
- package/dist/targets.d.ts +14 -0
- package/dist/targets.js +16 -0
- package/dist/vite.d.ts +7 -0
- package/dist/vite.js +81 -0
- package/dist/zod/index.d.ts +12 -3
- package/dist/zod/index.js +6 -5
- package/dist/zod/kinds.d.ts +3 -1
- package/dist/zod/kinds.js +11 -2
- package/dist/zod/rules.d.ts +1 -1
- package/docs/packages/mithaq/README.md +962 -0
- package/docs/packages/mithaq/src/asas/cli/README.md +252 -0
- package/docs/packages/mithaq/src/asas/runner/README.md +472 -0
- package/docs/packages/mithaq/src/drizzle/README.md +39 -0
- package/docs/packages/mithaq-admin/README.md +312 -0
- package/docs/templates/asas/README.md +1056 -0
- package/package.json +37 -8
- package/skills/add-field/SKILL.md +27 -0
- package/skills/add-kayan/SKILL.md +26 -0
- package/skills/eject-admin-piece/SKILL.md +28 -0
- package/skills/upgrade-mithaq/SKILL.md +35 -0
- package/r/mithaq-cells.json +0 -20
- package/r/mithaq-controls.json +0 -37
- package/r/mithaq-directional-icon.json +0 -15
- package/r/mithaq-expected-updated-at.json +0 -15
- package/r/mithaq-field-shell.json +0 -18
- package/r/mithaq-filter-operators.json +0 -15
- package/r/mithaq-form.json +0 -33
- package/r/mithaq-list-columns.json +0 -23
- package/r/mithaq-list-filters.json +0 -24
- package/r/mithaq-list-pagination.json +0 -24
- package/r/mithaq-list-query.json +0 -18
- package/r/mithaq-list-selection.json +0 -19
- package/r/mithaq-list.json +0 -33
- package/r/mithaq-nav.json +0 -24
- package/r/mithaq-nodes.json +0 -34
- package/r/mithaq-relationship-cell.json +0 -20
- package/r/mithaq-relationship-control.json +0 -20
- package/r/mithaq-rich-text.json +0 -40
- package/r/mithaq-screens-host.json +0 -96
- package/r/mithaq-storage.json +0 -44
- package/r/mithaq-upload.json +0 -29
- package/r/registry.json +0 -550
|
@@ -42,6 +42,9 @@ const fieldSource = (name, field) => {
|
|
|
42
42
|
const keys = [
|
|
43
43
|
field.required ? 'required: true' : '',
|
|
44
44
|
field.unique ? 'unique: true' : '',
|
|
45
|
+
// Better Auth's own migrations build it, so leaving it out would have the next migrate drop it.
|
|
46
|
+
// A unique column's constraint is already its index.
|
|
47
|
+
field.index && !field.unique ? 'index: true' : '',
|
|
45
48
|
field.onUpdate && field.type === 'date' ? 'nowOnUpdate: true' : '',
|
|
46
49
|
field.onUpdate ? "providedBy: { update: 'application' }" : '',
|
|
47
50
|
];
|
|
@@ -60,7 +63,7 @@ const fieldSource = (name, field) => {
|
|
|
60
63
|
const named = dbName === snakeCase(fieldName) ? '' : `, dbName: ${sourceString(dbName)}`;
|
|
61
64
|
return ` ${sourceKey(fieldName)}: { kind: '${kind}'${named}${rules ? `, rules: { ${rules} }` : ''} },`;
|
|
62
65
|
};
|
|
63
|
-
const kayanSource = ({ name, table, provider, providers, screens = {}, access = false, drawn, schemaName, idKind, }) => {
|
|
66
|
+
const kayanSource = ({ name, table, language, provider, providers, screens = {}, access = false, operations = false, drawn, schemaName, idKind, }) => {
|
|
64
67
|
const exported = exportName(name);
|
|
65
68
|
// Written only under screens that list the kayan: a `targets.<screen>` on a kayan
|
|
66
69
|
// the screen does not list is refused at build.
|
|
@@ -82,14 +85,16 @@ const kayanSource = ({ name, table, provider, providers, screens = {}, access =
|
|
|
82
85
|
const key = identifier.test(provider ?? '') ? `.${provider}` : `[${sourceString(provider ?? '')}]`;
|
|
83
86
|
const imports = [
|
|
84
87
|
`import { betterAuth, defaultFields, defaultOperations } from 'mithaq'`,
|
|
85
|
-
`import { defineKayan } from
|
|
88
|
+
`import { defineKayan } from ${sourceString(language)}`,
|
|
86
89
|
...(carries ? [`import { providers } from ${sourceString(providers ?? '')}`] : []),
|
|
87
90
|
...references.map((model) => `import { ${exportName(model)} } from './${exportName(model)}.ts'`),
|
|
88
91
|
...(access ? [`import { access } from './${exported}.access.ts'`] : []),
|
|
92
|
+
...(operations ? [`import { operations } from './${exported}.operations.ts'`] : []),
|
|
89
93
|
...(drawn ? [`import { screens } from './${exported}.screens.ts'`] : []),
|
|
90
94
|
];
|
|
91
95
|
if (!carries)
|
|
92
96
|
imports[0] = `import { defaultFields, defaultOperations } from 'mithaq'`;
|
|
97
|
+
const operationSource = operations ? '{ ...defaultOperations, ...operations }' : 'defaultOperations';
|
|
93
98
|
return `${header}
|
|
94
99
|
// This kayan's access and what each screen draws of it are the project's:
|
|
95
100
|
// \`${exported}.access.ts\` exporting \`access\` and \`${exported}.screens.ts\` exporting
|
|
@@ -102,7 +107,7 @@ export const ${exported} = defineKayan({
|
|
|
102
107
|
dbName: ${sourceString(dbName)}`},${schemaName === undefined ? '' : `
|
|
103
108
|
schemaName: ${sourceString(schemaName)},`}${carries ? `
|
|
104
109
|
auth: betterAuth({ server: providers${key}.server }),` : ''}
|
|
105
|
-
targets: { ${drawn ? `${titled.slice(2)}${titled ? ', ' : ''}...screens, service: { operations:
|
|
110
|
+
targets: { ${drawn ? `${titled.slice(2)}${titled ? ', ' : ''}...screens, service: { operations: ${operationSource} }` : `service: { operations: ${operationSource} }${titled}`} },${access ? `
|
|
106
111
|
access,` : ''}
|
|
107
112
|
fields: {
|
|
108
113
|
...defaultFields,
|
|
@@ -157,7 +162,7 @@ ${[...groups].map(([key, list]) => ` ${sourceKey(key)}: [${list.join(', ')}],`)
|
|
|
157
162
|
* Writes one kayan file per Better Auth table, and one barrel beside them. With `check`, writes
|
|
158
163
|
* nothing and returns the files a run would change or delete. A file without the header is never touched.
|
|
159
164
|
*/
|
|
160
|
-
export const generateAuth = async ({ options, directory, check = false, provider, providers, screens, schemaName, }) => {
|
|
165
|
+
export const generateAuth = async ({ options, directory, language, check = false, provider, providers, screens, schemaName, }) => {
|
|
161
166
|
const tables = getAuthTables(options);
|
|
162
167
|
const names = Object.keys(tables);
|
|
163
168
|
// With a `uuid` id, Better Auth's pg adapter leaves the id out of the insert and the
|
|
@@ -171,9 +176,9 @@ export const generateAuth = async ({ options, directory, check = false, provider
|
|
|
171
176
|
if (!check)
|
|
172
177
|
await mkdir(directory, { recursive: true });
|
|
173
178
|
const files = (await readFolder(directory)).map((entry) => entry.name);
|
|
174
|
-
//
|
|
179
|
+
// Sibling access, screens and operations files are the project's way in: the
|
|
175
180
|
// generated file is the generator's.
|
|
176
|
-
const siblings = new Set(files.filter((file) => file.endsWith('.access.ts') || file.endsWith('.screens.ts')));
|
|
181
|
+
const siblings = new Set(files.filter((file) => file.endsWith('.access.ts') || file.endsWith('.screens.ts') || file.endsWith('.operations.ts')));
|
|
177
182
|
// Read by importing it: a title written beside a block the sibling also writes is
|
|
178
183
|
// one the compiler refuses as overwritten.
|
|
179
184
|
const drawnBy = new Map();
|
|
@@ -185,7 +190,7 @@ export const generateAuth = async ({ options, directory, check = false, provider
|
|
|
185
190
|
}
|
|
186
191
|
const wanted = new Map(Object.entries(tables).map(([name, table]) => [
|
|
187
192
|
`${name}.ts`,
|
|
188
|
-
kayanSource({ name, table: table, provider, providers, screens, access: siblings.has(`${name}.access.ts`), drawn: drawnBy.get(`${name}.screens.ts`), schemaName, idKind }),
|
|
193
|
+
kayanSource({ name, table: table, language, provider, providers, screens, access: siblings.has(`${name}.access.ts`), operations: siblings.has(`${name}.operations.ts`), drawn: drawnBy.get(`${name}.screens.ts`), schemaName, idKind }),
|
|
189
194
|
]));
|
|
190
195
|
// Otherwise the `auth` key would be written onto nothing.
|
|
191
196
|
const named = Object.entries(tables).some(([name, table]) => (table.modelName ?? name) === provider);
|
|
@@ -253,6 +258,19 @@ export const run = async (project, check) => {
|
|
|
253
258
|
throw error;
|
|
254
259
|
throw new Refusal(`\`generate.providers\` names "${from}", which cannot be found from a generated file under ${sourceString(folder)}. Name a package this project depends on, or a path relative to a generated file.`);
|
|
255
260
|
}
|
|
261
|
+
// The language sits wherever the project's layout puts it, a package or a file, so nothing stands in for it either.
|
|
262
|
+
const language = generate?.language;
|
|
263
|
+
if (typeof language !== 'string') {
|
|
264
|
+
throw new Refusal(`src/mithaq.ts names no \`generate.language\`, which is the module each generated kayan imports \`defineKayan\` from. Add \`language: '<package name, or a path relative to a generated file>'\` to \`generate\`, beside \`auth: ${sourceString(folder)}\`.`);
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
createRequire(new URL('instance/index.ts', root)).resolve(language);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
if (error.code !== 'MODULE_NOT_FOUND')
|
|
271
|
+
throw error;
|
|
272
|
+
throw new Refusal(`\`generate.language\` names "${language}", which cannot be found from a generated file under ${sourceString(folder)}. Name the language's package, or a path relative to a generated file.`);
|
|
273
|
+
}
|
|
256
274
|
const { providers } = (await import(__rewriteRelativeImportExtension(pathToFileURL(resolved).href)));
|
|
257
275
|
if (!providers) {
|
|
258
276
|
throw new Refusal(`${from} exports no \`providers\`, which is the record this generator reads: one key per kayan a Better Auth instance authenticates, each \`{ options, server }\`.`);
|
|
@@ -274,7 +292,7 @@ export const run = async (project, check) => {
|
|
|
274
292
|
const changed = [];
|
|
275
293
|
for (const [provider, one] of Object.entries(providers)) {
|
|
276
294
|
const schemaName = one.schemaName;
|
|
277
|
-
const files = await generateAuth({ options: one.options, directory: new URL(`${provider}/`, root), check, provider, providers: from, screens, schemaName });
|
|
295
|
+
const files = await generateAuth({ options: one.options, directory: new URL(`${provider}/`, root), language, check, provider, providers: from, screens, schemaName });
|
|
278
296
|
changed.push(...files.map((file) => `${provider}/${file}`));
|
|
279
297
|
}
|
|
280
298
|
// A directory of an instance no longer declared is reported, never removed: the
|
|
@@ -81,12 +81,14 @@ export declare const betterAuth: <Server extends BetterAuth, Targets = unknown>(
|
|
|
81
81
|
server: (targets: Targets, vocabulary: {
|
|
82
82
|
catalog: readonly string[];
|
|
83
83
|
statements: Readonly<Record<string, readonly string[]>>;
|
|
84
|
+
referenced: (id: string) => Promise<number>;
|
|
84
85
|
}) => Server;
|
|
85
86
|
}) => {
|
|
86
87
|
provider: 'better-auth';
|
|
87
88
|
server: (targets: Targets, vocabulary: {
|
|
88
89
|
catalog: readonly string[];
|
|
89
90
|
statements: Readonly<Record<string, readonly string[]>>;
|
|
91
|
+
referenced: (id: string) => Promise<number>;
|
|
90
92
|
}) => Server;
|
|
91
93
|
};
|
|
92
94
|
export {};
|
|
@@ -3,16 +3,24 @@ import type { TenantTarget } from '../../mithaq/language.ts';
|
|
|
3
3
|
* The tenant target over Better Auth's organizations, laid down under `targets.tenant`.
|
|
4
4
|
* A tenant-owned kayan places its field, `organizationId: tenant.field`.
|
|
5
5
|
*/
|
|
6
|
-
export declare const betterAuthTenant: <const RelationTo extends string>({ relationTo, onDelete,
|
|
6
|
+
export declare const betterAuthTenant: <const RelationTo extends string>({ relationTo, onDelete, newRows, backoffice, organizations, }: {
|
|
7
7
|
/** The kayan a row's tenant is, Better Auth's `organization`. */
|
|
8
8
|
relationTo: RelationTo;
|
|
9
9
|
/** `restrict`: a tenant still holding a row is not deleted. */
|
|
10
10
|
onDelete: 'restrict';
|
|
11
|
+
/** A new row on a tenant-owned kayan takes the active organization, through an operation or a step's insert/upsert; naming another organization is refused. */
|
|
12
|
+
newRows: 'active';
|
|
11
13
|
/**
|
|
12
|
-
* Whether this actor is
|
|
14
|
+
* Whether this actor is backoffice staff in the trusted backoffice app, who may read every
|
|
13
15
|
* tenant with `tenant: 'all'`. The answer is the project's: it decides which app is trusted.
|
|
14
16
|
*/
|
|
15
|
-
|
|
17
|
+
backoffice?: (args: {
|
|
16
18
|
user: unknown;
|
|
17
19
|
}) => boolean | Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* How many organizations the project holds: `'one'` or `'many'`. Both keep the field and the
|
|
22
|
+
* scope; in `'one'` a `connect()` refuses to start over more than one. Whether a project shows its
|
|
23
|
+
* one organization is the project's own concern.
|
|
24
|
+
*/
|
|
25
|
+
organizations: 'one' | 'many';
|
|
18
26
|
}) => TenantTarget<RelationTo>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The tenant target over Better Auth's organizations, laid down under `targets.tenant`.
|
|
3
3
|
* A tenant-owned kayan places its field, `organizationId: tenant.field`.
|
|
4
4
|
*/
|
|
5
|
-
export const betterAuthTenant = ({ relationTo, onDelete,
|
|
5
|
+
export const betterAuthTenant = ({ relationTo, onDelete, newRows, backoffice, organizations, }) => {
|
|
6
6
|
if (typeof relationTo !== 'string' || !relationTo) {
|
|
7
7
|
throw new Error('betterAuthTenant needs relationTo, the name of the kayan a row\'s tenant is, such as "organization".');
|
|
8
8
|
}
|
|
@@ -11,11 +11,19 @@ export const betterAuthTenant = ({ relationTo, onDelete, platform, }) => {
|
|
|
11
11
|
if (onDelete !== 'restrict') {
|
|
12
12
|
throw new Error(`betterAuthTenant takes onDelete: 'restrict' alone, and was handed ${JSON.stringify(onDelete)}.`);
|
|
13
13
|
}
|
|
14
|
+
if (newRows !== 'active') {
|
|
15
|
+
throw new Error(`betterAuthTenant takes newRows: 'active' alone, and was handed ${JSON.stringify(newRows)}.`);
|
|
16
|
+
}
|
|
17
|
+
if (!['one', 'many'].includes(organizations)) {
|
|
18
|
+
throw new Error(`betterAuthTenant takes organizations: 'one' or 'many', and was handed ${JSON.stringify(organizations)}.`);
|
|
19
|
+
}
|
|
14
20
|
return {
|
|
15
21
|
capability: 'tenant',
|
|
16
22
|
relationTo,
|
|
17
23
|
onDelete,
|
|
18
|
-
|
|
24
|
+
newRows,
|
|
25
|
+
organizations,
|
|
26
|
+
...(backoffice && { backoffice }),
|
|
19
27
|
field: {
|
|
20
28
|
kind: 'relationship',
|
|
21
29
|
relationTo,
|
package/dist/auth/local/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { SignJWT, jwtVerify } from 'jose';
|
|
|
4
4
|
import { declaredFieldAccess } from '../../mithaq/rows.js';
|
|
5
5
|
import { providerOf } from '../../base/rules.js';
|
|
6
6
|
import { accessReport, isAdminActor, markAuthActor, resolveAssigned } from '../../asas/actor.js';
|
|
7
|
-
import { AsasError, codes, denied as refused, Notice } from '../../asas/runner/errors.js';
|
|
7
|
+
import { AsasError, codes, retryConflict, denied as refused, Notice } from '../../asas/runner/errors.js';
|
|
8
8
|
import { firstRegisters, running } from '../../asas/runner/runtime.js';
|
|
9
9
|
import { drain } from '../../asas/runner/write.js';
|
|
10
10
|
import { insert, neverRead, sealOf, update } from '../../asas/runner/steps.js';
|
|
@@ -721,8 +721,10 @@ export const localAuthStrategy = ({ options, kayan: resolved, target, writes, pr
|
|
|
721
721
|
}
|
|
722
722
|
}
|
|
723
723
|
catch (error) {
|
|
724
|
-
|
|
725
|
-
|
|
724
|
+
error = retryConflict(error);
|
|
725
|
+
if (error instanceof AsasError && error.key === 'error.conflict.retry')
|
|
726
|
+
return json({ code: codes[409], message: error.message, data: { key: error.key, params: error.params } }, { status: 409 });
|
|
727
|
+
// The driver detail may name another account, so other conflicts keep the existing refusal.
|
|
726
728
|
if (error instanceof AsasError && error.status === 409) {
|
|
727
729
|
return refusal(409, 'Email already exists.');
|
|
728
730
|
}
|
package/dist/base/fields.d.ts
CHANGED
|
@@ -51,7 +51,8 @@ export declare const defaultTimestampFields: {
|
|
|
51
51
|
};
|
|
52
52
|
/**
|
|
53
53
|
* The `id`, `updatedAt` and `createdAt` fields a kayan spreads into its own
|
|
54
|
-
* `fields`: `fields: { ...defaultFields, title }`. Spread order is column order
|
|
54
|
+
* `fields`: `fields: { ...defaultFields, title }`. Spread order is column order, except that
|
|
55
|
+
* a new table puts the two timestamps after every other column.
|
|
55
56
|
*/
|
|
56
57
|
export declare const defaultFields: {
|
|
57
58
|
/** A uuid Postgres fills when a create leaves it out; a client may send its own, so rows can be made offline. */
|
|
@@ -103,7 +104,7 @@ export declare const globalTypeLabel: {
|
|
|
103
104
|
};
|
|
104
105
|
/**
|
|
105
106
|
* The field a global spreads into its own `fields`:
|
|
106
|
-
* `fields: { ...defaultFields,
|
|
107
|
+
* `fields: { ...defaultFields, ...defaultGlobalFields, siteTitle }`.
|
|
107
108
|
*/
|
|
108
109
|
export declare const defaultGlobalFields: {
|
|
109
110
|
readonly globalType: {
|
package/dist/base/fields.js
CHANGED
|
@@ -33,13 +33,14 @@ export const defaultTimestampFields = {
|
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
35
35
|
* The `id`, `updatedAt` and `createdAt` fields a kayan spreads into its own
|
|
36
|
-
* `fields`: `fields: { ...defaultFields, title }`. Spread order is column order
|
|
36
|
+
* `fields`: `fields: { ...defaultFields, title }`. Spread order is column order, except that
|
|
37
|
+
* a new table puts the two timestamps after every other column.
|
|
37
38
|
*/
|
|
38
39
|
export const defaultFields = { ...defaultIdField, ...defaultTimestampFields };
|
|
39
40
|
export const globalTypeLabel = { en: 'Global type', ar: 'النوع العام' };
|
|
40
41
|
/**
|
|
41
42
|
* The field a global spreads into its own `fields`:
|
|
42
|
-
* `fields: { ...defaultFields,
|
|
43
|
+
* `fields: { ...defaultFields, ...defaultGlobalFields, siteTitle }`.
|
|
43
44
|
*/
|
|
44
45
|
export const defaultGlobalFields = {
|
|
45
46
|
globalType: {
|
package/dist/base/index.d.ts
CHANGED
|
@@ -15,13 +15,14 @@ export declare const base: {
|
|
|
15
15
|
relationship: import("./kinds.ts").Kind<string | number, "defaultValue" | "index" | "onDelete" | "providedBy" | "required" | "unique"> & {
|
|
16
16
|
pointsAtRow: true;
|
|
17
17
|
};
|
|
18
|
+
json: import("./kinds.ts").Kind<import("./kinds.ts").Json, "defaultValue" | "providedBy" | "required">;
|
|
18
19
|
join: import("./kinds.ts").Kind<unknown[], "providedBy" | "required">;
|
|
19
20
|
};
|
|
20
21
|
readonly rules: {
|
|
21
22
|
required: import("./rules.ts").Rule<boolean>;
|
|
22
23
|
unique: import("./rules.ts").Rule<boolean>;
|
|
23
24
|
index: import("./rules.ts").Rule<boolean>;
|
|
24
|
-
defaultValue: import("./rules.ts").Rule<
|
|
25
|
+
defaultValue: import("./rules.ts").Rule<Date | readonly unknown[] | import("../index.ts").Ref | import("./kinds.ts").Json>;
|
|
25
26
|
defaultNow: import("./rules.ts").Rule<boolean>;
|
|
26
27
|
nowOnUpdate: import("./rules.ts").Rule<boolean>;
|
|
27
28
|
providedBy: import("./rules.ts").Rule<Readonly<{
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ResolvedField } from '../mithaq/types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a value holds to a `json` field's `jsonSchema`. A schema that names draft-07, as Payload
|
|
4
|
+
* writes one it fetched, is read as draft-07; any other as 2020-12. Throws, naming the field, where
|
|
5
|
+
* the schema cannot be compiled, so a build refuses it rather than checking less than it says.
|
|
6
|
+
*/
|
|
7
|
+
export declare const jsonSchemaCheck: (field: ResolvedField) => ((value: unknown) => boolean);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Ajv, not zod's `fromJSONSchema`, which Payload uses: the converter passes over `required` without
|
|
2
|
+
// `properties`, and every keyword of a schema that names no `type`, so a value it waves through would
|
|
3
|
+
// break the schema the project wrote. Ajv holds a value to the whole schema, and refuses one it
|
|
4
|
+
// cannot hold a value to (an unknown keyword or format, a reference it cannot resolve) when it compiles.
|
|
5
|
+
import { Ajv } from 'ajv';
|
|
6
|
+
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
7
|
+
import addFormats from 'ajv-formats';
|
|
8
|
+
// One compile per schema, though a schema target shapes each field more than once.
|
|
9
|
+
const compiled = new WeakMap();
|
|
10
|
+
/**
|
|
11
|
+
* Whether a value holds to a `json` field's `jsonSchema`. A schema that names draft-07, as Payload
|
|
12
|
+
* writes one it fetched, is read as draft-07; any other as 2020-12. Throws, naming the field, where
|
|
13
|
+
* the schema cannot be compiled, so a build refuses it rather than checking less than it says.
|
|
14
|
+
*/
|
|
15
|
+
export const jsonSchemaCheck = (field) => {
|
|
16
|
+
const jsonSchema = field.jsonSchema;
|
|
17
|
+
const held = compiled.get(jsonSchema);
|
|
18
|
+
if (held)
|
|
19
|
+
return held;
|
|
20
|
+
const { schema } = jsonSchema;
|
|
21
|
+
// `strictTypes` and `strictTuples` only ask a schema to say more than JSON Schema requires.
|
|
22
|
+
const options = { strictTypes: false, strictTuples: false };
|
|
23
|
+
const ajv = typeof schema === 'object' && /^http:\/\/json-schema\.org\/draft-07\/schema#?$/.test(String(schema.$schema)) ? new Ajv(options) : new Ajv2020(options);
|
|
24
|
+
addFormats.default(ajv);
|
|
25
|
+
let validate;
|
|
26
|
+
try {
|
|
27
|
+
validate = ajv.compile(schema);
|
|
28
|
+
}
|
|
29
|
+
catch (thrown) {
|
|
30
|
+
throw new Error(`Kayan "${field.kayan}" field "${field.name}" jsonSchema cannot be checked: ${thrown.message}.`, { cause: thrown });
|
|
31
|
+
}
|
|
32
|
+
const check = (value) => validate(value) === true;
|
|
33
|
+
compiled.set(jsonSchema, check);
|
|
34
|
+
return check;
|
|
35
|
+
};
|
package/dist/base/kinds.d.ts
CHANGED
|
@@ -34,6 +34,26 @@ export declare const kind: <Value>(accepts: (value: unknown) => value is Value)
|
|
|
34
34
|
}) & (PointsAtRow extends true ? {
|
|
35
35
|
pointsAtRow: true;
|
|
36
36
|
} : unknown);
|
|
37
|
+
/** A value `JSON.parse` could hand back. */
|
|
38
|
+
export type Json = null | boolean | number | string | Json[] | {
|
|
39
|
+
[key: string]: Json;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The most UTF-8 bytes of JSON text a json field holds, which a field names as its `maxBytes`, so one write
|
|
43
|
+
* never stores a value every read of the row then carries.
|
|
44
|
+
*/
|
|
45
|
+
export declare const defaultJsonMaxBytes = 1000000;
|
|
46
|
+
/** How deep a json value nests: past it every reader that walks it, this check first, would run out of stack. */
|
|
47
|
+
export declare const jsonDepth = 64;
|
|
48
|
+
/** Text Postgres holds: no NUL, which no `text` or `jsonb` takes, and no half of a surrogate pair. */
|
|
49
|
+
export declare const isStorableText: (text: string) => boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Plain JSON, nested at most `jsonDepth` deep: no `undefined`, no number that is
|
|
52
|
+
* not finite, no `Date`, class instance or function, no cycle, and no string or key
|
|
53
|
+
* Postgres cannot store. A store would otherwise drop, rewrite or refuse it on the
|
|
54
|
+
* way in, and read back something else.
|
|
55
|
+
*/
|
|
56
|
+
export declare const isJson: (value: unknown, open?: object[]) => value is Json;
|
|
37
57
|
/** What the database does to this row when the row it points at is deleted. */
|
|
38
58
|
export type OnDelete = 'cascade' | 'restrict' | 'set null';
|
|
39
59
|
export declare const defaultKinds: {
|
|
@@ -50,5 +70,6 @@ export declare const defaultKinds: {
|
|
|
50
70
|
relationship: Kind<string | number, "defaultValue" | "index" | "onDelete" | "providedBy" | "required" | "unique"> & {
|
|
51
71
|
pointsAtRow: true;
|
|
52
72
|
};
|
|
73
|
+
json: Kind<Json, "defaultValue" | "providedBy" | "required">;
|
|
53
74
|
join: Kind<unknown[], "providedBy" | "required">;
|
|
54
75
|
};
|
package/dist/base/kinds.js
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
import { isDecimal } from './decimal.js';
|
|
2
2
|
/** Defines a project kind from its runtime check. */
|
|
3
3
|
export const kind = (accepts) => (definition) => ({ ...definition, value: undefined, accepts });
|
|
4
|
+
/**
|
|
5
|
+
* The most UTF-8 bytes of JSON text a json field holds, which a field names as its `maxBytes`, so one write
|
|
6
|
+
* never stores a value every read of the row then carries.
|
|
7
|
+
*/
|
|
8
|
+
export const defaultJsonMaxBytes = 1_000_000;
|
|
9
|
+
/** How deep a json value nests: past it every reader that walks it, this check first, would run out of stack. */
|
|
10
|
+
export const jsonDepth = 64;
|
|
11
|
+
/** Text Postgres holds: no NUL, which no `text` or `jsonb` takes, and no half of a surrogate pair. */
|
|
12
|
+
export const isStorableText = (text) => !text.includes('\u0000') && text.isWellFormed();
|
|
13
|
+
/**
|
|
14
|
+
* Plain JSON, nested at most `jsonDepth` deep: no `undefined`, no number that is
|
|
15
|
+
* not finite, no `Date`, class instance or function, no cycle, and no string or key
|
|
16
|
+
* Postgres cannot store. A store would otherwise drop, rewrite or refuse it on the
|
|
17
|
+
* way in, and read back something else.
|
|
18
|
+
*/
|
|
19
|
+
export const isJson = (value, open = []) => {
|
|
20
|
+
if (typeof value === 'string')
|
|
21
|
+
return isStorableText(value);
|
|
22
|
+
if (value === null || typeof value === 'boolean')
|
|
23
|
+
return true;
|
|
24
|
+
if (typeof value === 'number')
|
|
25
|
+
return Number.isFinite(value);
|
|
26
|
+
if (typeof value !== 'object' || open.length >= jsonDepth || open.includes(value))
|
|
27
|
+
return false;
|
|
28
|
+
const prototype = Object.getPrototypeOf(value);
|
|
29
|
+
if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null)
|
|
30
|
+
return false;
|
|
31
|
+
open.push(value);
|
|
32
|
+
// `Object.values` skips a hole, which `JSON.stringify` would write as `null`, so the length is checked too.
|
|
33
|
+
const plain = (Array.isArray(value) ? Object.keys(value).length === value.length : Object.keys(value).every(isStorableText))
|
|
34
|
+
&& Object.values(value).every((one) => isJson(one, open));
|
|
35
|
+
open.pop();
|
|
36
|
+
return plain;
|
|
37
|
+
};
|
|
4
38
|
export const defaultKinds = {
|
|
5
39
|
text: kind((value) => typeof value === 'string')({
|
|
6
40
|
label: { en: 'Text', ar: 'نص' },
|
|
@@ -66,7 +100,18 @@ export const defaultKinds = {
|
|
|
66
100
|
message: { en: 'Pick one of the listed entries.', ar: 'اختر عنصرًا من القائمة.' },
|
|
67
101
|
rules: ['providedBy', 'required', 'unique', 'index', 'defaultValue', 'onDelete'],
|
|
68
102
|
pointsAtRow: true,
|
|
69
|
-
|
|
103
|
+
// A row another still names is never deleted from under it, its drafts included; a field says
|
|
104
|
+
// `set null` or `cascade` to let it go.
|
|
105
|
+
onDelete: 'restrict',
|
|
106
|
+
}),
|
|
107
|
+
// Payload's `json` field, unwrapped, so each reads a row the other wrote. Nothing orders by one:
|
|
108
|
+
// jsonb's order is no order a person reads.
|
|
109
|
+
json: kind(isJson)({
|
|
110
|
+
label: { en: 'JSON', ar: 'JSON' },
|
|
111
|
+
message: { en: 'This is not JSON this field takes.', ar: 'هذه ليست بيانات JSON يقبلها هذا الحقل.' },
|
|
112
|
+
rules: ['providedBy', 'required', 'defaultValue'],
|
|
113
|
+
operators: ['equals', 'not_equals', 'in', 'not_in', 'exists', 'like', 'contains'],
|
|
114
|
+
sortable: false,
|
|
70
115
|
}),
|
|
71
116
|
// The rows of another kayan whose `on` relationship points at this one. It builds
|
|
72
117
|
// no column: the id lives on the child, and a read populates it through the child's access.
|
package/dist/base/rules.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Ref } from '../mithaq/user.ts';
|
|
2
2
|
import type { Localized } from '../mithaq/types.ts';
|
|
3
|
-
import type
|
|
3
|
+
import { type Json, type OnDelete } from './kinds.ts';
|
|
4
4
|
export type Rule<Value = unknown> = {
|
|
5
5
|
/** Compile-time value carried by a binding; never read at runtime. */
|
|
6
6
|
value: Value;
|
|
@@ -33,8 +33,8 @@ export declare const defaultRules: {
|
|
|
33
33
|
required: Rule<boolean>;
|
|
34
34
|
unique: Rule<boolean>;
|
|
35
35
|
index: Rule<boolean>;
|
|
36
|
-
/** A literal, or `user.<key>` for the actor's value. Each row gets its own copy. */
|
|
37
|
-
defaultValue: Rule<
|
|
36
|
+
/** A literal, JSON for a `json` field, or `user.<key>` for the actor's value. Each row gets its own copy. */
|
|
37
|
+
defaultValue: Rule<Date | readonly unknown[] | Ref | Json>;
|
|
38
38
|
/** The moment of the write, on create. */
|
|
39
39
|
defaultNow: Rule<boolean>;
|
|
40
40
|
/** The moment of the write, on every update, whatever the column is called. */
|
|
@@ -63,7 +63,7 @@ export declare const defaultRules: {
|
|
|
63
63
|
uppercase: Rule<boolean>;
|
|
64
64
|
lowercase: Rule<boolean>;
|
|
65
65
|
trim: Rule<boolean>;
|
|
66
|
-
/** What happens to this row when the row it points at is deleted. Left out, a required
|
|
66
|
+
/** What happens to this row when the row it points at is deleted. Left out, a required field and a set restrict and any other takes its kind's default. */
|
|
67
67
|
onDelete: Rule<OnDelete>;
|
|
68
68
|
};
|
|
69
69
|
/** The rule keys a field may carry, derived from the record the language names. */
|
package/dist/base/rules.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isRef } from '../mithaq/user.js';
|
|
2
|
+
import { isJson } from './kinds.js';
|
|
2
3
|
/** Defines a project rule from its runtime check. */
|
|
3
4
|
export const rule = ({ accepts, message, excludes, }) => ({ value: undefined, accepts, message, excludes });
|
|
4
5
|
const isBoolean = (value) => typeof value === 'boolean';
|
|
@@ -14,9 +15,9 @@ export const defaultRules = {
|
|
|
14
15
|
required: rule({ accepts: isBoolean, message: { en: 'This field is required.', ar: 'هذا الحقل مطلوب.' } }),
|
|
15
16
|
unique: boolean,
|
|
16
17
|
index: boolean,
|
|
17
|
-
/** A literal, or `user.<key>` for the actor's value. Each row gets its own copy. */
|
|
18
|
+
/** A literal, JSON for a `json` field, or `user.<key>` for the actor's value. Each row gets its own copy. */
|
|
18
19
|
defaultValue: rule({
|
|
19
|
-
accepts: (value) => value instanceof Date || isRef(value) || Array.isArray(value) ||
|
|
20
|
+
accepts: (value) => value instanceof Date || isRef(value) || Array.isArray(value) || (value !== null && isJson(value)),
|
|
20
21
|
excludes: ['defaultNow'],
|
|
21
22
|
}),
|
|
22
23
|
/** The moment of the write, on create. */
|
|
@@ -73,7 +74,7 @@ export const defaultRules = {
|
|
|
73
74
|
uppercase: rule({ accepts: isBoolean, excludes: ['lowercase'] }),
|
|
74
75
|
lowercase: rule({ accepts: isBoolean, excludes: ['uppercase'] }),
|
|
75
76
|
trim: boolean,
|
|
76
|
-
/** What happens to this row when the row it points at is deleted. Left out, a required
|
|
77
|
+
/** What happens to this row when the row it points at is deleted. Left out, a required field and a set restrict and any other takes its kind's default. */
|
|
77
78
|
onDelete: rule({
|
|
78
79
|
accepts: (value) => typeof value === 'string' && ['cascade', 'restrict', 'set null'].includes(value),
|
|
79
80
|
}),
|
package/dist/drizzle/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type SQL } from 'drizzle-orm';
|
|
2
2
|
import { type NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
3
|
-
import { pgTable, type AnyPgColumnBuilder, type PgBooleanBuilder, type PgBuildColumns, type PgEnum, type PgDateStringBuilder, type PgIntegerBuilder, type PgNumericBuilder, type PgNumericNumberBuilder, type PgSchema, type PgSerialBuilder, type PgTable, type PgTableWithColumns, type PgTextBuilder, type PgTimestampBuilder, type PgUUIDBuilder, type PgVarcharBuilder, type SetDimensions, type SetHasDefault, type SetNotNull } from 'drizzle-orm/pg-core';
|
|
3
|
+
import { pgTable, type AnyPgColumnBuilder, type PgBooleanBuilder, type PgBuildColumns, type PgEnum, type PgDateStringBuilder, type PgIntegerBuilder, type PgJsonbBuilder, type PgNumericBuilder, type PgNumericNumberBuilder, type PgSchema, type PgSerialBuilder, type PgTable, type PgTableWithColumns, type PgTextBuilder, type PgTimestampBuilder, type PgUUIDBuilder, type PgVarcharBuilder, type SetDimensions, type SetHasDefault, type SetNotNull } from 'drizzle-orm/pg-core';
|
|
4
4
|
import type { ResolvedField, ResolvedKayan, RuntimeField } from '../mithaq/types.ts';
|
|
5
5
|
import type { Store } from '../mithaq/language.ts';
|
|
6
6
|
import type { Operator } from '../mithaq/where.ts';
|
|
@@ -65,6 +65,8 @@ type ShippedBuilder<F, List extends Registry, Name> = F extends {
|
|
|
65
65
|
} ? PgTimestampBuilder : F extends {
|
|
66
66
|
kind: 'date';
|
|
67
67
|
} ? PgDateStringBuilder : F extends {
|
|
68
|
+
kind: 'json';
|
|
69
|
+
} ? PgJsonbBuilder : F extends {
|
|
68
70
|
rules: {
|
|
69
71
|
maxLength: number;
|
|
70
72
|
};
|
|
@@ -166,6 +168,8 @@ export declare const manyFields: (kayan: ResolvedKayan | undefined) => ({
|
|
|
166
168
|
readonly value: string;
|
|
167
169
|
readonly label: Readonly<Record<string, string>>;
|
|
168
170
|
}[] | undefined;
|
|
171
|
+
jsonSchema?: import("../mithaq/types.ts").JsonSchema;
|
|
172
|
+
maxBytes?: number;
|
|
169
173
|
dbName?: string;
|
|
170
174
|
targets?: Record<string, unknown>;
|
|
171
175
|
custom?: Record<string, unknown>;
|
|
@@ -174,6 +178,7 @@ export declare const manyFields: (kayan: ResolvedKayan | undefined) => ({
|
|
|
174
178
|
rules?: Record<string, unknown>;
|
|
175
179
|
relationTo?: string | (() => import("../mithaq/types.ts").Kayan);
|
|
176
180
|
hasMany?: true;
|
|
181
|
+
filterOptions?: import("../mithaq/types.ts").FilterOptions;
|
|
177
182
|
on?: string;
|
|
178
183
|
defaultLimit?: number;
|
|
179
184
|
defaultSort?: string;
|
|
@@ -188,7 +193,7 @@ export declare const manyFields: (kayan: ResolvedKayan | undefined) => ({
|
|
|
188
193
|
operators?: readonly Operator[];
|
|
189
194
|
sortable?: false;
|
|
190
195
|
filesIn?: readonly string[];
|
|
191
|
-
richText?: (value: unknown) => import("../rich-text/
|
|
196
|
+
richText?: (value: unknown) => import("../rich-text/value.ts").RichTextRead;
|
|
192
197
|
target?: ResolvedKayan;
|
|
193
198
|
})[];
|
|
194
199
|
export declare const schemaOf: (table: unknown) => string;
|
|
@@ -267,141 +272,6 @@ export declare const recordMigration: ({ db, hash, createdAt, name }: {
|
|
|
267
272
|
createdAt: number;
|
|
268
273
|
name: string;
|
|
269
274
|
}) => Promise<void>;
|
|
270
|
-
/** The drizzle store target's maps: a map per kind and a map per rule. */
|
|
271
|
-
export declare const drizzleTarget: {
|
|
272
|
-
readonly capability: 'store';
|
|
273
|
-
readonly type: 'drizzle';
|
|
274
|
-
readonly idKinds: ("number" | "text" | "uuid")[];
|
|
275
|
-
readonly kinds: {
|
|
276
|
-
number: (field: ResolvedField) => PgNumericNumberBuilder | PgSerialBuilder;
|
|
277
|
-
integer: (field: ResolvedField) => PgIntegerBuilder;
|
|
278
|
-
decimal: (field: ResolvedField) => PgNumericBuilder;
|
|
279
|
-
text: (field: ResolvedField) => PgTextBuilder<[string, ...string[]]> | PgVarcharBuilder<[string, ...string[]]>;
|
|
280
|
-
email: (field: ResolvedField) => PgTextBuilder<[string, ...string[]]> | PgVarcharBuilder<[string, ...string[]]>;
|
|
281
|
-
checkbox: (field: ResolvedField) => PgBooleanBuilder;
|
|
282
|
-
textArray: (field: ResolvedField) => SetDimensions<PgTextBuilder<[string, ...string[]]>, 1>;
|
|
283
|
-
uuid: (field: ResolvedField) => PgUUIDBuilder;
|
|
284
|
-
datetime: (field: ResolvedField) => PgTimestampBuilder;
|
|
285
|
-
date: (field: ResolvedField) => PgDateStringBuilder;
|
|
286
|
-
join: (field: ResolvedField) => never;
|
|
287
|
-
relationship: ColumnBuilder;
|
|
288
|
-
};
|
|
289
|
-
readonly rules: {
|
|
290
|
-
primary: ({ column }: {
|
|
291
|
-
column: Modifiable;
|
|
292
|
-
value: undefined;
|
|
293
|
-
field: ResolvedField;
|
|
294
|
-
}) => Modifiable;
|
|
295
|
-
required: ({ column, value: on }: {
|
|
296
|
-
column: Modifiable;
|
|
297
|
-
value: boolean;
|
|
298
|
-
field: ResolvedField;
|
|
299
|
-
}) => Modifiable;
|
|
300
|
-
unique: ({ column }: {
|
|
301
|
-
column: Modifiable;
|
|
302
|
-
value: boolean;
|
|
303
|
-
field: ResolvedField;
|
|
304
|
-
}) => Modifiable;
|
|
305
|
-
index: ({ column }: {
|
|
306
|
-
column: Modifiable;
|
|
307
|
-
value: boolean;
|
|
308
|
-
field: ResolvedField;
|
|
309
|
-
}) => Modifiable;
|
|
310
|
-
defaultValue: ({ column, value }: {
|
|
311
|
-
column: Modifiable;
|
|
312
|
-
value: string | number | boolean | Date | readonly unknown[] | import("../index.ts").Ref;
|
|
313
|
-
field: ResolvedField;
|
|
314
|
-
}) => Modifiable;
|
|
315
|
-
defaultNow: ({ column, value: on, field }: {
|
|
316
|
-
column: Modifiable;
|
|
317
|
-
value: boolean;
|
|
318
|
-
field: ResolvedField;
|
|
319
|
-
}) => any;
|
|
320
|
-
nowOnUpdate: ({ column }: {
|
|
321
|
-
column: Modifiable;
|
|
322
|
-
value: boolean;
|
|
323
|
-
field: ResolvedField;
|
|
324
|
-
}) => Modifiable;
|
|
325
|
-
providedBy: ({ column }: {
|
|
326
|
-
column: Modifiable;
|
|
327
|
-
value: Readonly<{
|
|
328
|
-
create?: import("../base/rules.ts").Provider;
|
|
329
|
-
update?: import("../base/rules.ts").Provider;
|
|
330
|
-
}>;
|
|
331
|
-
field: ResolvedField;
|
|
332
|
-
}) => Modifiable;
|
|
333
|
-
onDelete: ({ column }: {
|
|
334
|
-
column: Modifiable;
|
|
335
|
-
value: import("../index.ts").OnDelete;
|
|
336
|
-
field: ResolvedField;
|
|
337
|
-
}) => Modifiable;
|
|
338
|
-
localized: ({ column }: {
|
|
339
|
-
column: Modifiable;
|
|
340
|
-
value: boolean;
|
|
341
|
-
field: ResolvedField;
|
|
342
|
-
}) => Modifiable;
|
|
343
|
-
maxLength: ({ column }: {
|
|
344
|
-
column: Modifiable;
|
|
345
|
-
value: number;
|
|
346
|
-
field: ResolvedField;
|
|
347
|
-
}) => Modifiable;
|
|
348
|
-
precision: ({ column }: {
|
|
349
|
-
column: Modifiable;
|
|
350
|
-
value: number;
|
|
351
|
-
field: ResolvedField;
|
|
352
|
-
}) => Modifiable;
|
|
353
|
-
scale: ({ column }: {
|
|
354
|
-
column: Modifiable;
|
|
355
|
-
value: number;
|
|
356
|
-
field: ResolvedField;
|
|
357
|
-
}) => Modifiable;
|
|
358
|
-
minLength: ({ column }: {
|
|
359
|
-
column: Modifiable;
|
|
360
|
-
value: number;
|
|
361
|
-
field: ResolvedField;
|
|
362
|
-
}) => Modifiable;
|
|
363
|
-
pattern: ({ column }: {
|
|
364
|
-
column: Modifiable;
|
|
365
|
-
value: RegExp;
|
|
366
|
-
field: ResolvedField;
|
|
367
|
-
}) => Modifiable;
|
|
368
|
-
min: ({ column }: {
|
|
369
|
-
column: Modifiable;
|
|
370
|
-
value: string | number;
|
|
371
|
-
field: ResolvedField;
|
|
372
|
-
}) => Modifiable;
|
|
373
|
-
max: ({ column }: {
|
|
374
|
-
column: Modifiable;
|
|
375
|
-
value: string | number;
|
|
376
|
-
field: ResolvedField;
|
|
377
|
-
}) => Modifiable;
|
|
378
|
-
minRows: ({ column }: {
|
|
379
|
-
column: Modifiable;
|
|
380
|
-
value: number;
|
|
381
|
-
field: ResolvedField;
|
|
382
|
-
}) => Modifiable;
|
|
383
|
-
maxRows: ({ column }: {
|
|
384
|
-
column: Modifiable;
|
|
385
|
-
value: number;
|
|
386
|
-
field: ResolvedField;
|
|
387
|
-
}) => Modifiable;
|
|
388
|
-
uppercase: ({ column }: {
|
|
389
|
-
column: Modifiable;
|
|
390
|
-
value: boolean;
|
|
391
|
-
field: ResolvedField;
|
|
392
|
-
}) => Modifiable;
|
|
393
|
-
lowercase: ({ column }: {
|
|
394
|
-
column: Modifiable;
|
|
395
|
-
value: boolean;
|
|
396
|
-
field: ResolvedField;
|
|
397
|
-
}) => Modifiable;
|
|
398
|
-
trim: ({ column }: {
|
|
399
|
-
column: Modifiable;
|
|
400
|
-
value: boolean;
|
|
401
|
-
field: ResolvedField;
|
|
402
|
-
}) => Modifiable;
|
|
403
|
-
};
|
|
404
|
-
};
|
|
405
275
|
type DrizzleAdapterArguments<Kinds, Rules> = {
|
|
406
276
|
/** A ready drizzle instance, so the pool, logger and options are the developer's. */
|
|
407
277
|
db?: Database;
|
|
@@ -409,8 +279,8 @@ type DrizzleAdapterArguments<Kinds, Rules> = {
|
|
|
409
279
|
connectionString?: string;
|
|
410
280
|
kinds: Kinds;
|
|
411
281
|
rules: Rules;
|
|
412
|
-
/** The kinds this store builds an id of;
|
|
413
|
-
idKinds
|
|
282
|
+
/** The kinds this store builds an id of; `drizzleIdKinds`, unless you replace the kinds map and its relationship builder. */
|
|
283
|
+
idKinds: readonly string[];
|
|
414
284
|
/** Push the schema on connect instead of running migrations. */
|
|
415
285
|
push?: boolean;
|
|
416
286
|
/**
|