tina4-nodejs 3.13.147 → 3.13.149

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.
Files changed (45) hide show
  1. package/CLAUDE.md +4 -3
  2. package/package.json +1 -1
  3. package/packages/cli/dist/bin.js +10901 -9855
  4. package/packages/cli/src/commands/generate.ts +184 -25
  5. package/packages/core/dist/index.js +10874 -9828
  6. package/packages/core/src/devAdmin.ts +72 -1
  7. package/packages/core/src/dispatchPipeline.ts +4 -12
  8. package/packages/core/src/index.ts +3 -0
  9. package/packages/core/src/messenger.ts +57 -1
  10. package/packages/core/src/router.ts +11 -1
  11. package/packages/core/src/server.ts +5 -2
  12. package/packages/core/src/session.ts +45 -3
  13. package/packages/core/templates/crud/form.twig +26 -0
  14. package/packages/core/templates/crud/modals.twig +31 -0
  15. package/packages/core/templates/crud/page.twig +401 -0
  16. package/packages/core/templates/crud/table.twig +69 -0
  17. package/packages/frond/dist/index.js +9 -1
  18. package/packages/frond/src/engine.ts +9 -0
  19. package/packages/frond/src/index.ts +1 -1
  20. package/packages/orm/dist/index.js +1283 -227
  21. package/packages/orm/src/adapters/mongodb.ts +209 -34
  22. package/packages/orm/src/autoCrud.ts +9 -0
  23. package/packages/orm/src/crud.ts +273 -0
  24. package/packages/orm/src/crudHelpers.ts +194 -0
  25. package/packages/orm/src/crudRender.ts +202 -0
  26. package/packages/orm/src/crudTable.ts +171 -0
  27. package/packages/orm/src/index.ts +7 -1
  28. package/packages/orm/src/migration.ts +167 -4
  29. package/packages/orm/src/query.ts +99 -40
  30. package/packages/orm/src/sqlTranslator.ts +8 -1
  31. package/packages/orm/src/types.ts +19 -0
  32. package/types/core/src/devAdmin.d.ts +23 -0
  33. package/types/core/src/messenger.d.ts +4 -0
  34. package/types/core/src/router.d.ts +0 -5
  35. package/types/core/src/session.d.ts +25 -0
  36. package/types/frond/src/engine.d.ts +6 -0
  37. package/types/frond/src/index.d.ts +1 -1
  38. package/types/orm/src/crud.d.ts +82 -0
  39. package/types/orm/src/crudHelpers.d.ts +63 -0
  40. package/types/orm/src/crudRender.d.ts +38 -0
  41. package/types/orm/src/crudTable.d.ts +45 -0
  42. package/types/orm/src/index.d.ts +3 -1
  43. package/types/orm/src/migration.d.ts +0 -18
  44. package/types/orm/src/query.d.ts +0 -12
  45. package/types/orm/src/types.d.ts +19 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Crud (ADR-0094) — the pure helpers behind the HTML admin page.
3
+ *
4
+ * No I/O, no templates, no DB: column/label/alignment derivation, safe-sort
5
+ * resolution, the pre-joined table-data hash, the pagination controls, the
6
+ * injected JS config, and the two linear SQL-listing parsers. Kept in their own
7
+ * module so `crud.ts` stays a thin orchestrator and no single file carries the
8
+ * whole feature (the second-pass split by concern).
9
+ */
10
+ import { escapeHtml } from "../../frond/src/engine.js";
11
+ /** A BaseModel-like class: the statics Crud reads + the query statics it calls. */
12
+ export interface CrudModel {
13
+ tableName: string;
14
+ fields: Record<string, {
15
+ type?: string;
16
+ primaryKey?: boolean;
17
+ [k: string]: unknown;
18
+ }>;
19
+ fieldMapping?: Record<string, string>;
20
+ softDelete?: boolean;
21
+ tableFilter?: string;
22
+ name?: string;
23
+ all(limit?: number, offset?: number, include?: string[], orderBy?: string): Promise<any>;
24
+ where(conditions: string, params?: unknown[], limit?: number, offset?: number, include?: string[], orderBy?: string): Promise<any>;
25
+ count(conditions?: string, params?: unknown[]): Promise<number>;
26
+ }
27
+ /** Escape alias used throughout the CRUD HTML assembly. */
28
+ export declare const h: typeof escapeHtml;
29
+ /** The DB column a property name maps to (fieldMapping or the property itself). */
30
+ export declare function columnOf(model: CrudModel, prop: string): string;
31
+ /** The primary-key property name (the field flagged primaryKey, else "id"). */
32
+ export declare function primaryKeyProp(model: CrudModel): string;
33
+ /** The model's declared string/text property names. */
34
+ export declare function searchableProps(model: CrudModel): string[];
35
+ /** Column alignment from the field type: numeric right, everything else left. */
36
+ export declare function columnAlignment(model: CrudModel | null, col: string): string;
37
+ /** Pretty label from a column name: "user_name" => "User Name". */
38
+ export declare function prettyLabel(col: string): string;
39
+ /**
40
+ * ADR-0069 safe sort: resolve the requested sort to a declared field PROPERTY
41
+ * (the grid's headers + client state are keyed by property), else the primary
42
+ * key. A request may name the field by its property or its mapped column; the
43
+ * DB column for the ORDER BY is derived with columnOf(). A rendered page ignores
44
+ * a bad sort rather than erroring.
45
+ */
46
+ export declare function crudSortProp(model: CrudModel, requested: string | undefined, pkProp: string): string;
47
+ /** Read one column value from a record hash (keyed by property name). */
48
+ export declare function cellValue(record: Record<string, unknown>, col: string): unknown;
49
+ /** Build the pre-joined <td> run for one row (every value escaped + aligned). */
50
+ export declare function buildCells(columns: string[], record: Record<string, unknown>, editable: boolean, aligns: string[]): string;
51
+ /**
52
+ * Drop a trailing ORDER BY / LIMIT clause from each line of a custom listing SQL.
53
+ * Pure string scanning — no backtracking regex.
54
+ */
55
+ export declare function stripOrderAndLimit(sql: string): string;
56
+ /**
57
+ * The column names a custom listing SQL selects (for the search CAST LIKE).
58
+ * Linear string scan: locate the first SELECT / FROM keywords (whitespace
59
+ * bounded, case-insensitive) and split the projection on commas — no
60
+ * backtracking regex over caller-supplied SQL (ReDoS-safe), mirroring the
61
+ * Ruby master's plain-string SQL parsing.
62
+ */
63
+ export declare function extractColumns(sql: string): string[];
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Render a crud/<name>.twig template, app-first-then-framework: an app override
3
+ * at src/templates/crud/<name>.twig wins over the framework's shipped copy (the
4
+ * same resolution the error pages use).
5
+ */
6
+ export declare function renderCrudTemplate(name: string, data: Record<string, unknown>): string;
7
+ /** Render the create/edit/delete modal shell (crud/modals.twig). */
8
+ export declare function renderModals(editableColumns: string[], pk: string): string;
9
+ /**
10
+ * Render an HTML table fragment from an array of record hashes via crud/table.twig.
11
+ * Inline-editable (contenteditable cells + Save/Delete wired by the template's
12
+ * delegated listener). Synchronous — no model, no DB.
13
+ */
14
+ export declare function generateTable(records: Array<Record<string, unknown>> | null | undefined, options?: {
15
+ tableName?: string;
16
+ primaryKey?: string;
17
+ editable?: boolean;
18
+ }): string;
19
+ export interface FormFieldDef {
20
+ name: string;
21
+ type?: string;
22
+ label?: string;
23
+ value?: unknown;
24
+ required?: boolean;
25
+ options?: Array<{
26
+ value: unknown;
27
+ label: unknown;
28
+ }>;
29
+ }
30
+ /**
31
+ * Render an HTML form from a field-definition array via crud/form.twig.
32
+ * Synchronous — no model, no DB.
33
+ */
34
+ export declare function generateForm(fields: FormFieldDef[], options?: {
35
+ action?: string;
36
+ method?: string;
37
+ tableName?: string;
38
+ }): string;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Crud (ADR-0094) — table + pagination assembly.
3
+ *
4
+ * The sort links, the pagination controls, the injected JS config, and the
5
+ * pre-joined table-data hash the crud/table.twig template consumes. Split from
6
+ * crudHelpers so neither file carries the whole feature (one concern per file).
7
+ */
8
+ import { type CrudModel } from "./crudHelpers.js";
9
+ export declare function sortIndicator(sortCol: string, col: string, sortDir: string): string;
10
+ export declare function sortUrl(requestPath: string, col: string, nextDir: string, page: number, search: string, limit: number): string;
11
+ export declare function pageUrl(requestPath: string, p: number, search: string, sortCol: string, sortDir: string, limit: number): string;
12
+ /** One flat list of pagination controls (Prev, numbered pages, Next). */
13
+ export declare function pageControls(page: number, totalPages: number, requestPath: string, search: string, sortCol: string, sortDir: string, limit: number): Array<Record<string, unknown>>;
14
+ /** The JSON config injected into the page's nonce'd <script>. */
15
+ export declare function jsConfig(opts: {
16
+ apiPath: string;
17
+ pk: string;
18
+ columns: string[];
19
+ editable: string[];
20
+ model: CrudModel;
21
+ limit: number;
22
+ search: string;
23
+ sortCol: string;
24
+ sortDir: string;
25
+ page: number;
26
+ }): string;
27
+ export interface TableDataOpts {
28
+ columns: string[];
29
+ records: Array<Record<string, unknown>>;
30
+ pk: string;
31
+ tableName: string;
32
+ editable: boolean;
33
+ sortable: boolean;
34
+ inlineScript: boolean;
35
+ requestPath: string;
36
+ search: string;
37
+ sortCol: string;
38
+ sortDir: string;
39
+ page: number;
40
+ limit: number;
41
+ tableId: string | null;
42
+ model: CrudModel | null;
43
+ }
44
+ /** Build the data hash crud/table.twig consumes (headers + pre-joined rows). */
45
+ export declare function tableData(opts: TableDataOpts): Record<string, unknown>;
@@ -15,7 +15,9 @@ export { syncModels, ensureMigrationTable, getNextBatch, isMigrationApplied, rec
15
15
  export type { MigrationResult, MigrationStatus } from "./migration.js";
16
16
  export { AutoCrud, generateCrudRoutes, crudEligibleModels } from "./autoCrud.js";
17
17
  export type { AutoCrudOptions } from "./autoCrud.js";
18
- export { buildQuery, parseQueryString, UnknownFieldError, InvalidQueryParameterError } from "./query.js";
18
+ export { Crud, CRUD, toCrud, registerBackend, generateTable, generateForm, stripOrderAndLimit, _resetCrudRegistrations } from "./crud.js";
19
+ export type { ToCrudOptions, CrudModel, FormFieldDef } from "./crud.js";
20
+ export { buildQuery, parseQueryString, resolveField, resolveFieldColumn, UnknownFieldError, InvalidQueryParameterError } from "./query.js";
19
21
  export { validate } from "./validation.js";
20
22
  export type { ValidationError } from "./validation.js";
21
23
  export { BaseModel, snakeToCamel, camelToSnake } from "./baseModel.js";
@@ -181,24 +181,6 @@ export declare function splitStatements(sql: string, delimiter?: string): string
181
181
  * AFTER the numbered ones, then lexically. Mirrors Python's `_migration_sort_key`.
182
182
  */
183
183
  export declare function sortMigrationFiles(files: string[]): string[];
184
- /**
185
- * Run all pending SQL-file migrations.
186
- *
187
- * Supports both naming patterns:
188
- * - Sequential: 000001_description.sql
189
- * - Timestamp: YYYYMMDDHHMMSS_description.sql
190
- *
191
- * 1. Creates the `tina4_migration` tracking table if it doesn't exist.
192
- * 2. Scans `migrationsDir` for `.sql` files (excluding `.down.sql`), sorted.
193
- * 3. Skips files already recorded as applied.
194
- * 4. Splits file content on `delimiter` and executes each statement.
195
- * 5. On success records the migration with the current batch number.
196
- * 6. On error logs and continues.
197
- * 7. Returns a summary of applied / skipped / failed files.
198
- *
199
- * @param adapter - A DatabaseAdapter instance (or omit to use the global adapter).
200
- * @param options - Optional configuration.
201
- */
202
184
  export declare function migrate(adapter?: DatabaseAdapter, options?: {
203
185
  migrationsDir?: string;
204
186
  delimiter?: string;
@@ -36,18 +36,6 @@ export declare class UnknownFieldError extends Error {
36
36
  export declare class InvalidQueryParameterError extends Error {
37
37
  constructor(message: string);
38
38
  }
39
- /**
40
- * Build the list SQL from parsed query options.
41
- *
42
- * `quote` renders every table/column name the builder emits - pass the bound
43
- * adapter's dialect (`(name) => quoteIdentifier(adapter, name)`); the default is
44
- * the ANSI `"name"`.
45
- *
46
- * Every filter key and sort field is passed through `resolveColumn` (see
47
- * resolveFieldColumn) and only the column it returns is emitted; a key it does
48
- * not resolve throws UnknownFieldError before any SQL exists. With no
49
- * resolver, no filter or sort key resolves (ADR-0069).
50
- */
51
39
  export declare function buildQuery(tableName: string, options: QueryOptions, extraConditions?: string[], quote?: (name: string) => string, resolveColumn?: (key: string) => string | null): {
52
40
  sql: string;
53
41
  countSql: string;
@@ -187,4 +187,23 @@ export interface QueryOptions {
187
187
  sort?: string;
188
188
  page?: number;
189
189
  limit?: number;
190
+ /**
191
+ * ADR-0094: full-text search term. LIKE %term% is OR'd across `searchColumns`
192
+ * and added to the WHERE before limit/offset, so the total reflects the
193
+ * filtered set. The CRUD admin grid (and any client) drives this via ?search=.
194
+ */
195
+ search?: string;
196
+ /**
197
+ * DB columns the `search` term matches against (the model's declared
198
+ * string/text columns, already resolved to their mapped column names). The
199
+ * caller resolves these from the model; buildQuery only quotes and binds them,
200
+ * so nothing but a declared column can reach SQL (ADR-0069).
201
+ */
202
+ searchColumns?: string[];
203
+ /**
204
+ * ADR-0094: direction for a single bare ?sort=column (the CRUD grid's
205
+ * spelling). "desc" sorts descending; anything else ascending. Ignored for a
206
+ * Mongo-style "-field,field" sort, which carries its own inline direction.
207
+ */
208
+ sortDir?: string;
190
209
  }