nucleus-core-ts 0.10.11 → 0.10.13
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/dist/.build-ok +1 -1
- package/dist/index.js +14 -14
- package/dist/server.js +14 -14
- package/dist/src/Nucleus/routes/entity/searchCondition.d.ts +52 -0
- package/dist/src/Nucleus/schemaReconcile.d.ts +69 -1
- package/package.json +124 -124
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type SQL } from 'drizzle-orm';
|
|
2
|
+
/**
|
|
3
|
+
* What a list search actually asks the database.
|
|
4
|
+
*
|
|
5
|
+
* The whole search term used to be handed to every searchable column at once:
|
|
6
|
+
*
|
|
7
|
+
* first_name ILIKE '%Ayşe Yılmaz%' OR last_name ILIKE '%Ayşe Yılmaz%' OR …
|
|
8
|
+
*
|
|
9
|
+
* No single column holds a first name and a last name together, so typing a
|
|
10
|
+
* person's full name — the most ordinary thing anyone does in a staff directory
|
|
11
|
+
* — matched nobody. Each half found her on its own; the space is what emptied
|
|
12
|
+
* the screen. Measured on a real installation: "Ayşe" 1 row, "Yılmaz" 1 row,
|
|
13
|
+
* "Ayşe Yılmaz" 0 rows.
|
|
14
|
+
*
|
|
15
|
+
* So the term is split, and the shape is inverted where it matters:
|
|
16
|
+
*
|
|
17
|
+
* AND over words( OR over columns( column contains word ) )
|
|
18
|
+
*
|
|
19
|
+
* Every word must appear SOMEWHERE, not all in the same column. That also makes
|
|
20
|
+
* word order irrelevant, which is right: nobody should have to know whether the
|
|
21
|
+
* directory stores "Ayşe Yılmaz" or "Yılmaz, Ayşe".
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* A ceiling on how many words one search may ask about.
|
|
25
|
+
*
|
|
26
|
+
* Each word costs one comparison per searchable column, so a pasted paragraph
|
|
27
|
+
* would turn a single request into hundreds of full-column scans. Ten is far
|
|
28
|
+
* past any real name or title and cheap to serve; the rest of the term is
|
|
29
|
+
* ignored rather than refused, because a search box that answers nothing at all
|
|
30
|
+
* is worse than one that answers the first ten words.
|
|
31
|
+
*/
|
|
32
|
+
export declare const MAX_SEARCH_TOKENS = 10;
|
|
33
|
+
/** The words a search term is asking about, in order, capped. */
|
|
34
|
+
export declare function searchTokens(term: string): string[];
|
|
35
|
+
/**
|
|
36
|
+
* The same fold the SQL applies, in TypeScript, so both sides of the comparison
|
|
37
|
+
* are normalised identically.
|
|
38
|
+
*
|
|
39
|
+
* The Turkish letters are replaced BEFORE lowering because JavaScript lowers
|
|
40
|
+
* `İ` to `i` followed by a combining dot — two code points that then match
|
|
41
|
+
* nothing.
|
|
42
|
+
*/
|
|
43
|
+
export declare function foldForSearch(value: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* The WHERE fragment for `?search=…&searchFields=…`, or null when there is
|
|
46
|
+
* nothing to ask.
|
|
47
|
+
*
|
|
48
|
+
* Null rather than a condition that matches everything: the caller pushes onto a
|
|
49
|
+
* list of conditions, and a term of nothing but spaces must not narrow the list
|
|
50
|
+
* NOR silently drop other conditions.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildSearchCondition(columns: SQL[], term: string): SQL | null;
|
|
@@ -59,6 +59,15 @@ export type ExpectedColumn = {
|
|
|
59
59
|
sqlType: string;
|
|
60
60
|
notNull: boolean;
|
|
61
61
|
hasDefault: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* The default as SQL, when it is a plain literal — `'startsWith'`, `42`, `true`.
|
|
64
|
+
*
|
|
65
|
+
* Only literals. A `sql\`…\`` default or a function call is left undefined and
|
|
66
|
+
* the column is reported rather than repaired: rendering one wrong writes a
|
|
67
|
+
* WRONG default, which is worse than none because the push would then see the
|
|
68
|
+
* column as settled and never revisit it.
|
|
69
|
+
*/
|
|
70
|
+
defaultLiteral?: string;
|
|
62
71
|
};
|
|
63
72
|
export type ExpectedTable = {
|
|
64
73
|
name: string;
|
|
@@ -66,6 +75,16 @@ export type ExpectedTable = {
|
|
|
66
75
|
};
|
|
67
76
|
/** What the database actually has: table name → column names. */
|
|
68
77
|
export type ActualSchema = Readonly<Record<string, ReadonlySet<string>>>;
|
|
78
|
+
/**
|
|
79
|
+
* Which columns the database already gives a default, table by table.
|
|
80
|
+
*
|
|
81
|
+
* Kept beside `ActualSchema` rather than folded into it: that type is a set of
|
|
82
|
+
* NAMES and is what every caller and every test already passes. Widening it
|
|
83
|
+
* would make a column's absence and a column's default the same question, and
|
|
84
|
+
* they are answered at different times — the name decides whether to ADD, the
|
|
85
|
+
* default decides whether to SET.
|
|
86
|
+
*/
|
|
87
|
+
export type ActualDefaults = Readonly<Record<string, ReadonlySet<string>>>;
|
|
69
88
|
export type Drift = {
|
|
70
89
|
/** Tables the code declares that the database does not have at all. */
|
|
71
90
|
missingTables: string[];
|
|
@@ -74,6 +93,22 @@ export type Drift = {
|
|
|
74
93
|
table: string;
|
|
75
94
|
column: ExpectedColumn;
|
|
76
95
|
}[];
|
|
96
|
+
/**
|
|
97
|
+
* Columns that EXIST but do not carry the default the model declares.
|
|
98
|
+
*
|
|
99
|
+
* Invisible until now, and the gap cost a live cutover. `claims.mode` is
|
|
100
|
+
* declared `notNull` with a default; the column was already there, so the
|
|
101
|
+
* name check below passed it and nothing looked further. The push would
|
|
102
|
+
* normally have set it — `isAdditiveStatement` allows SET DEFAULT precisely
|
|
103
|
+
* because it touches nothing stored — but a push that ABORTS applies no
|
|
104
|
+
* statements at all, and this repair is what runs in its place. It put the
|
|
105
|
+
* table back and left the default off, so the 2,715 claim inserts that
|
|
106
|
+
* followed all failed 23502 and the install booted with no permissions.
|
|
107
|
+
*/
|
|
108
|
+
columnsMissingDefault: {
|
|
109
|
+
table: string;
|
|
110
|
+
column: ExpectedColumn;
|
|
111
|
+
}[];
|
|
77
112
|
};
|
|
78
113
|
/**
|
|
79
114
|
* What the code expects minus what the database has.
|
|
@@ -83,7 +118,9 @@ export type Drift = {
|
|
|
83
118
|
* another application owns, and dropping it is exactly the destructive act this
|
|
84
119
|
* refuses to perform.
|
|
85
120
|
*/
|
|
86
|
-
export declare function findDrift(expected: readonly ExpectedTable[], actual: ActualSchema
|
|
121
|
+
export declare function findDrift(expected: readonly ExpectedTable[], actual: ActualSchema,
|
|
122
|
+
/** Omit and no default drift is reported — every existing caller keeps its answer. */
|
|
123
|
+
actualDefaults?: ActualDefaults): Drift;
|
|
87
124
|
/**
|
|
88
125
|
* The statement that adds one column, or null when it cannot be added safely.
|
|
89
126
|
*
|
|
@@ -97,6 +134,14 @@ export declare function findDrift(expected: readonly ExpectedTable[], actual: Ac
|
|
|
97
134
|
* column missing.
|
|
98
135
|
*/
|
|
99
136
|
export declare function addColumnStatement(schema: string, table: string, column: ExpectedColumn): string | null;
|
|
137
|
+
/**
|
|
138
|
+
* `alter column … set default …`, or null when the default cannot be rendered.
|
|
139
|
+
*
|
|
140
|
+
* Safe to run unattended for the same reason the push is allowed to run it:
|
|
141
|
+
* a default decides what a FUTURE insert gets when it omits the column. No
|
|
142
|
+
* stored row changes, and no write that worked a moment ago starts failing.
|
|
143
|
+
*/
|
|
144
|
+
export declare function setDefaultStatement(schema: string, table: string, column: ExpectedColumn): string | null;
|
|
100
145
|
export type RepairPlan = {
|
|
101
146
|
statements: string[];
|
|
102
147
|
/** Columns that exist in the model and cannot be added without a value. */
|
|
@@ -191,10 +236,33 @@ type ReconcileLogger = {
|
|
|
191
236
|
* list is worse than none — it would report a clean schema over a broken one.
|
|
192
237
|
*/
|
|
193
238
|
export declare function expectedFromTables(tables: Record<string, unknown>): ExpectedTable[];
|
|
239
|
+
/**
|
|
240
|
+
* A default as SQL, for the three shapes that can be rendered without guessing.
|
|
241
|
+
*
|
|
242
|
+
* Anything else — `sql\`now()\``, an object, an array, a function — returns
|
|
243
|
+
* undefined, and the column is then reported rather than repaired. The
|
|
244
|
+
* asymmetry is deliberate: a missing default is a bug that announces itself
|
|
245
|
+
* (23502 on the first insert), while a WRONG default is silent and permanent,
|
|
246
|
+
* because once the column has one the push stops proposing the right one.
|
|
247
|
+
*/
|
|
248
|
+
export declare function renderDefaultLiteral(value: unknown): string | undefined;
|
|
194
249
|
/** What the database has right now, for the tables asked about. */
|
|
195
250
|
export declare function readActualSchema(execute: (statement: string) => Promise<{
|
|
196
251
|
rows?: unknown[];
|
|
197
252
|
} | unknown[]>, schema: string): Promise<ActualSchema>;
|
|
253
|
+
/**
|
|
254
|
+
* The same read, plus which columns already carry a default.
|
|
255
|
+
*
|
|
256
|
+
* One query, not two: `column_default` costs nothing extra to select and the
|
|
257
|
+
* caller needs both answers at the same moment. `readActualSchema` delegates
|
|
258
|
+
* here so there is one statement to keep correct rather than two.
|
|
259
|
+
*/
|
|
260
|
+
export declare function readActualSchemaDetailed(execute: (statement: string) => Promise<{
|
|
261
|
+
rows?: unknown[];
|
|
262
|
+
} | unknown[]>, schema: string): Promise<{
|
|
263
|
+
columns: ActualSchema;
|
|
264
|
+
withDefault: ActualDefaults;
|
|
265
|
+
}>;
|
|
198
266
|
/**
|
|
199
267
|
* Puts back the columns a failed push did not add.
|
|
200
268
|
*
|
package/package.json
CHANGED
|
@@ -1,126 +1,126 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
2
|
+
"name": "nucleus-core-ts",
|
|
3
|
+
"version": "0.10.13",
|
|
4
|
+
"description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
|
|
5
|
+
"author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"backend",
|
|
9
|
+
"framework",
|
|
10
|
+
"drizzle",
|
|
11
|
+
"typescript"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
"bin",
|
|
15
|
+
"dist",
|
|
16
|
+
"schemas",
|
|
17
|
+
"scripts",
|
|
18
|
+
"infra/templates",
|
|
19
|
+
"infra/scripts/generate-project.ts",
|
|
20
|
+
"src/system.tables.json",
|
|
21
|
+
"src/Services/SchemaTables/resolveEntities.ts",
|
|
22
|
+
"src/Services/Logger/auditTaxonomy.ts",
|
|
23
|
+
"public",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"module": "dist/index.js",
|
|
27
|
+
"type": "module",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./server": {
|
|
35
|
+
"types": "./dist/server.d.ts",
|
|
36
|
+
"import": "./dist/server.js"
|
|
37
|
+
},
|
|
38
|
+
"./client": {
|
|
39
|
+
"types": "./dist/client.d.ts",
|
|
40
|
+
"import": "./dist/client.js"
|
|
41
|
+
},
|
|
42
|
+
"./fe": {
|
|
43
|
+
"types": "./dist/fe/index.d.ts",
|
|
44
|
+
"import": "./dist/fe/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./fe/auth": {
|
|
47
|
+
"types": "./dist/fe/components/AuthGuard/index.d.ts",
|
|
48
|
+
"import": "./dist/fe/components/AuthGuard/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./proxy": {
|
|
51
|
+
"types": "./dist/src/Client/Proxy/index.d.ts",
|
|
52
|
+
"import": "./dist/src/Client/Proxy/index.js"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"test": "bun test src scripts fe",
|
|
57
|
+
"build": "bun run scripts/build.ts",
|
|
58
|
+
"build:quick": "bun run build:js && bun run build:types",
|
|
59
|
+
"build:js": "bun build ./index.ts ./server.ts ./client.ts ./fe/index.ts ./src/Client/Proxy/index.ts --outdir=dist --target=bun --format=esm --splitting --minify --external react --external react-dom --external gsap --external @gsap/react --external h-state --external drizzle-orm --external drizzle-kit --external ioredis --external googleapis --external @dapr/dapr --external pg --external @xyflow/react --external @xyflow/system --external @azure/communication-email --external nodemailer --external @azure/identity --external stripe --external sharp",
|
|
60
|
+
"build:types": "tsc --declaration --emitDeclarationOnly --outDir dist --skipLibCheck",
|
|
61
|
+
"version:patch": "bun run scripts/version.ts patch",
|
|
62
|
+
"version:minor": "bun run scripts/version.ts minor",
|
|
63
|
+
"version:major": "bun run scripts/version.ts major",
|
|
64
|
+
"publish:npm": "bun run scripts/publish.ts",
|
|
65
|
+
"publish:dry": "bun run scripts/publish.ts --dry-run",
|
|
66
|
+
"release": "bun run version:patch && bun run publish:npm",
|
|
67
|
+
"release:minor": "bun run version:minor && bun run publish:npm",
|
|
68
|
+
"release:major": "bun run version:major && bun run publish:npm",
|
|
69
|
+
"generate:schemas": "bun run scripts/generate-types-schema.ts",
|
|
70
|
+
"lint": "biome check . --write",
|
|
71
|
+
"lint:fix": "biome check --config-path ./biome.json --write",
|
|
72
|
+
"prepublishOnly": "bun run scripts/verify-publishable.ts"
|
|
73
|
+
},
|
|
74
|
+
"bin": {
|
|
75
|
+
"nucleus-core-ts": "./bin/cli.ts",
|
|
76
|
+
"nucleus": "./bin/cli.ts",
|
|
77
|
+
"nucleus-generate": "./bin/cli.ts"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@biomejs/biome": "2.5.10",
|
|
81
|
+
"googleapis": "^171.4.0",
|
|
82
|
+
"@types/bun": "latest",
|
|
83
|
+
"@types/pg": "latest",
|
|
84
|
+
"@types/react": "latest",
|
|
85
|
+
"@happy-dom/global-registrator": "^20.11.2",
|
|
86
|
+
"@types/react-dom": "latest",
|
|
87
|
+
"bun-plugin-tailwind": "latest",
|
|
88
|
+
"drizzle-kit": "latest",
|
|
89
|
+
"javascript-obfuscator": "latest",
|
|
90
|
+
"lucide-react": "latest",
|
|
91
|
+
"nodemailer": "^9.0.3"
|
|
92
|
+
},
|
|
93
|
+
"peerDependencies": {
|
|
94
|
+
"typescript": "latest",
|
|
95
|
+
"@dapr/dapr": "latest",
|
|
96
|
+
"react": "latest",
|
|
97
|
+
"react-dom": "latest",
|
|
98
|
+
"pg": "latest",
|
|
99
|
+
"tailwindcss": "latest",
|
|
100
|
+
"tailwind-merge": "latest",
|
|
101
|
+
"clsx": "latest",
|
|
102
|
+
"googleapis": "*"
|
|
103
|
+
},
|
|
104
|
+
"dependencies": {
|
|
105
|
+
"@sinclair/typebox": "0.34.47",
|
|
106
|
+
"@gsap/react": "latest",
|
|
107
|
+
"@simplewebauthn/server": "^13.3.0",
|
|
108
|
+
"@xyflow/react": "latest",
|
|
109
|
+
"drizzle-orm": "latest",
|
|
110
|
+
"gsap": "latest",
|
|
111
|
+
"h-state": "latest",
|
|
112
|
+
"ioredis": "latest",
|
|
113
|
+
"reflect-metadata": "^0.2.2"
|
|
114
|
+
},
|
|
115
|
+
"optionalDependencies": {
|
|
116
|
+
"@azure/communication-email": "latest",
|
|
117
|
+
"@marsaud/smb2": "^0.18.0",
|
|
118
|
+
"sharp": "^0.33.0",
|
|
119
|
+
"stripe": "latest"
|
|
120
|
+
},
|
|
121
|
+
"peerDependenciesMeta": {
|
|
122
|
+
"googleapis": {
|
|
123
|
+
"optional": true
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
126
|
}
|