tina4-nodejs 3.13.133 → 3.13.135
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/CLAUDE.md +3 -3
- package/README.md +2 -2
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +3213 -3062
- package/packages/cli/src/commands/generate.ts +33 -22
- package/packages/cli/src/commands/lint.ts +77 -111
- package/packages/core/dist/index.js +3122 -2963
- package/packages/core/src/.tina4-metrics.json +15004 -0
- package/packages/core/src/aiClient.ts +199 -161
- package/packages/core/src/devAdmin.ts +46 -14
- package/packages/core/src/dispatchPipeline.ts +65 -67
- package/packages/core/src/docs.ts +52 -544
- package/packages/core/src/docsParser.ts +270 -0
- package/packages/core/src/docsScanner.ts +121 -0
- package/packages/core/src/docsSignatures.ts +165 -0
- package/packages/core/src/index.ts +2 -0
- package/packages/core/src/logger.ts +68 -82
- package/packages/core/src/mcp.ts +32 -60
- package/packages/core/src/messenger.ts +136 -157
- package/packages/core/src/middleware.ts +56 -60
- package/packages/core/src/plan.ts +78 -70
- package/packages/core/src/projectIndex.ts +15 -288
- package/packages/core/src/projectIndexExtractors.ts +126 -0
- package/packages/core/src/projectIndexStorage.ts +122 -0
- package/packages/core/src/push.ts +293 -0
- package/packages/core/src/server.ts +187 -183
- package/packages/frond/dist/index.js +607 -770
- package/packages/frond/src/engine.ts +670 -818
- package/packages/orm/dist/index.js +3132 -2976
- package/packages/orm/src/adapters/mongodb.ts +99 -144
- package/packages/orm/src/baseModel.ts +429 -515
- package/packages/orm/src/fakeData.ts +73 -61
- package/packages/orm/src/migration.ts +96 -126
- package/packages/orm/src/seeder.ts +6 -238
- package/packages/orm/src/seederTable.ts +101 -0
- package/packages/orm/src/seederTypes.ts +14 -0
- package/packages/orm/src/validation.ts +97 -80
- package/types/core/src/aiClient.d.ts +5 -0
- package/types/core/src/devAdmin.d.ts +23 -1
- package/types/core/src/docsParser.d.ts +28 -0
- package/types/core/src/docsScanner.d.ts +1 -0
- package/types/core/src/docsSignatures.d.ts +11 -0
- package/types/core/src/index.d.ts +2 -0
- package/types/core/src/messenger.d.ts +8 -0
- package/types/core/src/projectIndexExtractors.d.ts +3 -0
- package/types/core/src/projectIndexStorage.d.ts +13 -0
- package/types/core/src/push.d.ts +45 -0
- package/types/frond/src/engine.d.ts +25 -0
- package/types/orm/src/fakeData.d.ts +3 -0
- package/types/orm/src/seeder.d.ts +3 -89
- package/types/orm/src/seederTable.d.ts +9 -0
- package/types/orm/src/seederTypes.d.ts +16 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { FakeData } from "./fakeData.js";
|
|
2
|
+
import { adapterExecute, adapterColumns, adapterInsert } from "./database.js";
|
|
3
|
+
import { Log } from "../../core/src/index.js";
|
|
4
|
+
import type { DatabaseAdapter, FieldDefinition, FieldType } from "./types.js";
|
|
5
|
+
import type { SeedOptions, SeedSummary } from "./seederTypes.js";
|
|
6
|
+
|
|
7
|
+
function normaliseOptions(overrides?: Record<string, unknown>, opts?: SeedOptions):
|
|
8
|
+
Required<Pick<SeedOptions, "clear" | "strict">> & { overrides?: Record<string, unknown> } {
|
|
9
|
+
const merged = { ...(opts ?? {}) };
|
|
10
|
+
return {
|
|
11
|
+
overrides: merged.overrides ?? overrides,
|
|
12
|
+
clear: merged.clear ?? false,
|
|
13
|
+
strict: merged.strict ?? false,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Delete every row, logging but not hiding a clear failure. */
|
|
18
|
+
export async function clearTable(db: DatabaseAdapter, tableName: string): Promise<void> {
|
|
19
|
+
try {
|
|
20
|
+
await adapterExecute(db, `DELETE FROM "${tableName}"`);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
Log.warning(`Seeder: could not clear '${tableName}': ${(e as Error).message}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function sqlTypeToFieldType(sqlType: string): FieldType {
|
|
27
|
+
const type = (sqlType || "").toUpperCase();
|
|
28
|
+
if (type.includes("INT")) return "integer";
|
|
29
|
+
if (type.includes("BOOL")) return "boolean";
|
|
30
|
+
if (["REAL", "FLOA", "DOUB", "NUM", "DEC"].some((part) => type.includes(part))) return "number";
|
|
31
|
+
if (type.includes("DATE") || type.includes("TIME")) return "datetime";
|
|
32
|
+
if (type.includes("TEXT") || type.includes("CLOB")) return "text";
|
|
33
|
+
return "string";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Build generators from live table metadata for the explicit seedTable path. */
|
|
37
|
+
export async function autoFieldMap(
|
|
38
|
+
db: DatabaseAdapter,
|
|
39
|
+
table: string,
|
|
40
|
+
fake: FakeData = new FakeData(),
|
|
41
|
+
): Promise<Record<string, () => unknown>> {
|
|
42
|
+
const columns = await adapterColumns(db, table);
|
|
43
|
+
const fieldMap: Record<string, () => unknown> = {};
|
|
44
|
+
for (const column of columns) {
|
|
45
|
+
const name = column.name;
|
|
46
|
+
const sqlType = String(column.type ?? "").toUpperCase();
|
|
47
|
+
const generatedPk = column.primaryKey === true &&
|
|
48
|
+
(sqlType.includes("AUTO") || sqlType.includes("SERIAL") || sqlType.includes("IDENTITY") || name.toLowerCase() === "id");
|
|
49
|
+
if (generatedPk) continue;
|
|
50
|
+
fieldMap[name] = () => fake.forField({ type: sqlTypeToFieldType(sqlType) }, name, table);
|
|
51
|
+
}
|
|
52
|
+
return fieldMap;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Seed a table through the adapter insert path, counting or re-raising row failures. */
|
|
56
|
+
export async function seedTable(
|
|
57
|
+
db: DatabaseAdapter,
|
|
58
|
+
tableName: string,
|
|
59
|
+
count = 10,
|
|
60
|
+
fieldMap?: Record<string, (() => unknown) | unknown>,
|
|
61
|
+
overrides?: Record<string, unknown>,
|
|
62
|
+
opts?: SeedOptions,
|
|
63
|
+
): Promise<SeedSummary> {
|
|
64
|
+
if (opts?.seed !== undefined) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
"seedTable() no longer accepts opts.seed: it has no generators of its own to seed " +
|
|
67
|
+
"(fieldMap callables are opaque). Build a seeded FakeData yourself and close over it " +
|
|
68
|
+
"in fieldMap, e.g. const fake = new FakeData(42); seedTable(db, table, count, " +
|
|
69
|
+
"{ name: () => fake.name() }).",
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const { overrides: effectiveOverrides, clear, strict } = normaliseOptions(overrides, opts);
|
|
73
|
+
if (!fieldMap || Object.keys(fieldMap).length === 0) return { seeded: 0, failed: 0, errors: [] };
|
|
74
|
+
if (clear) await clearTable(db, tableName);
|
|
75
|
+
|
|
76
|
+
let seeded = 0;
|
|
77
|
+
let failed = 0;
|
|
78
|
+
const errors: Array<{ row: number; message: string }> = [];
|
|
79
|
+
for (let i = 0; i < count; i++) {
|
|
80
|
+
try {
|
|
81
|
+
const row: Record<string, unknown> = {};
|
|
82
|
+
for (const [column, generator] of Object.entries(fieldMap)) {
|
|
83
|
+
row[column] = typeof generator === "function" ? (generator as () => unknown)() : generator;
|
|
84
|
+
}
|
|
85
|
+
for (const [column, value] of Object.entries(effectiveOverrides ?? {})) row[column] = value;
|
|
86
|
+
await adapterInsert(db, tableName, row);
|
|
87
|
+
seeded++;
|
|
88
|
+
} catch (e) {
|
|
89
|
+
const message = (e as Error).message ?? String(e);
|
|
90
|
+
if (strict) {
|
|
91
|
+
Log.error(`Seeder: row ${i} failed seeding '${tableName}' (strict): ${message}`);
|
|
92
|
+
throw e;
|
|
93
|
+
}
|
|
94
|
+
failed++;
|
|
95
|
+
errors.push({ row: i, message });
|
|
96
|
+
Log.warning(`Seeder: row ${i} failed seeding '${tableName}', skipped: ${message}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
Log.info(`Seeder: '${tableName}' — seeded ${seeded}, ${failed} failed`);
|
|
100
|
+
return { seeded, failed, errors };
|
|
101
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Result of a seed run. */
|
|
2
|
+
export interface SeedSummary {
|
|
3
|
+
seeded: number;
|
|
4
|
+
failed: number;
|
|
5
|
+
errors: Array<{ row: number; message: string }>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Options shared by the table and ORM seed paths. */
|
|
9
|
+
export interface SeedOptions {
|
|
10
|
+
overrides?: Record<string, unknown>;
|
|
11
|
+
clear?: boolean;
|
|
12
|
+
seed?: number;
|
|
13
|
+
strict?: boolean;
|
|
14
|
+
}
|
|
@@ -5,6 +5,101 @@ export interface ValidationError {
|
|
|
5
5
|
message: string;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
function error(field: string, message: string): ValidationError {
|
|
9
|
+
return { field, message };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function validateString(
|
|
13
|
+
name: string,
|
|
14
|
+
value: string,
|
|
15
|
+
def: FieldDefinition,
|
|
16
|
+
pattern: RegExp | undefined,
|
|
17
|
+
): ValidationError[] {
|
|
18
|
+
const errors: ValidationError[] = [];
|
|
19
|
+
if (def.minLength !== undefined && value.length < def.minLength) {
|
|
20
|
+
errors.push(error(name, `must be at least ${def.minLength} characters`));
|
|
21
|
+
}
|
|
22
|
+
if (def.maxLength !== undefined && value.length > def.maxLength) {
|
|
23
|
+
errors.push(error(name, `must be at most ${def.maxLength} characters`));
|
|
24
|
+
}
|
|
25
|
+
if (pattern && !pattern.test(value)) {
|
|
26
|
+
errors.push(error(name, "does not match the required format"));
|
|
27
|
+
}
|
|
28
|
+
return errors;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validateNumber(name: string, value: unknown, def: FieldDefinition): ValidationError[] {
|
|
32
|
+
const num = typeof value === "string" ? Number(value) : value;
|
|
33
|
+
if (typeof num !== "number" || isNaN(num)) return [error(name, "must be a number")];
|
|
34
|
+
|
|
35
|
+
const errors: ValidationError[] = [];
|
|
36
|
+
if (def.type === "integer" && !Number.isInteger(num)) {
|
|
37
|
+
errors.push(error(name, "must be an integer"));
|
|
38
|
+
}
|
|
39
|
+
if (def.min !== undefined && num < def.min) {
|
|
40
|
+
errors.push(error(name, `must be at least ${def.min}`));
|
|
41
|
+
}
|
|
42
|
+
if (def.max !== undefined && num > def.max) {
|
|
43
|
+
errors.push(error(name, `must be at most ${def.max}`));
|
|
44
|
+
}
|
|
45
|
+
return errors;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function validateBoolean(name: string, value: unknown): ValidationError[] {
|
|
49
|
+
return typeof value === "boolean" || value === 0 || value === 1 || value === "true" || value === "false"
|
|
50
|
+
? []
|
|
51
|
+
: [error(name, "must be a boolean")];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function validateDatetime(name: string, value: unknown): ValidationError[] {
|
|
55
|
+
return typeof value === "string" && isNaN(Date.parse(value))
|
|
56
|
+
? [error(name, "must be a valid date/time")]
|
|
57
|
+
: [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function validateJson(name: string, value: unknown): ValidationError[] {
|
|
61
|
+
return value !== null && typeof value !== "object" && typeof value !== "string"
|
|
62
|
+
? [error(name, "must be a JSON object or array")]
|
|
63
|
+
: [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function validateForeignKey(name: string, value: unknown): ValidationError[] {
|
|
67
|
+
const fkNum = typeof value === "string" ? Number(value) : value;
|
|
68
|
+
return typeof fkNum !== "number" || isNaN(fkNum) || !Number.isInteger(fkNum)
|
|
69
|
+
? [error(name, "must be a valid foreign key (integer)")]
|
|
70
|
+
: [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function validateField(
|
|
74
|
+
name: string,
|
|
75
|
+
value: unknown,
|
|
76
|
+
def: FieldDefinition,
|
|
77
|
+
pattern: RegExp | undefined,
|
|
78
|
+
): ValidationError[] {
|
|
79
|
+
if (def.type === "string" || def.type === "text") {
|
|
80
|
+
return typeof value === "string"
|
|
81
|
+
? validateString(name, value, def, pattern)
|
|
82
|
+
: [error(name, "must be a string")];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (def.type === "integer" || def.type === "number" || def.type === "numeric") {
|
|
86
|
+
return validateNumber(name, value, def);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
switch (def.type) {
|
|
90
|
+
case "boolean":
|
|
91
|
+
return validateBoolean(name, value);
|
|
92
|
+
case "datetime":
|
|
93
|
+
return validateDatetime(name, value);
|
|
94
|
+
case "json":
|
|
95
|
+
return validateJson(name, value);
|
|
96
|
+
case "foreignKey":
|
|
97
|
+
return validateForeignKey(name, value);
|
|
98
|
+
default:
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
8
103
|
export function validate(
|
|
9
104
|
data: Record<string, unknown>,
|
|
10
105
|
fields: Record<string, FieldDefinition>,
|
|
@@ -28,92 +123,14 @@ export function validate(
|
|
|
28
123
|
|
|
29
124
|
// Required check (skip on update if field not provided)
|
|
30
125
|
if (def.required && !isUpdate && (value === undefined || value === null || value === "")) {
|
|
31
|
-
errors.push(
|
|
126
|
+
errors.push(error(name, "is required"));
|
|
32
127
|
continue;
|
|
33
128
|
}
|
|
34
129
|
|
|
35
130
|
// Skip further validation if value not provided
|
|
36
131
|
if (value === undefined || value === null) continue;
|
|
37
132
|
|
|
38
|
-
|
|
39
|
-
switch (def.type) {
|
|
40
|
-
case "string":
|
|
41
|
-
case "text":
|
|
42
|
-
if (typeof value !== "string") {
|
|
43
|
-
errors.push({ field: name, message: "must be a string" });
|
|
44
|
-
} else {
|
|
45
|
-
if (def.minLength !== undefined && value.length < def.minLength) {
|
|
46
|
-
errors.push({ field: name, message: `must be at least ${def.minLength} characters` });
|
|
47
|
-
}
|
|
48
|
-
if (def.maxLength !== undefined && value.length > def.maxLength) {
|
|
49
|
-
errors.push({ field: name, message: `must be at most ${def.maxLength} characters` });
|
|
50
|
-
}
|
|
51
|
-
const regex = compiledPatterns.get(name);
|
|
52
|
-
if (regex && !regex.test(value)) {
|
|
53
|
-
// Feature 19 (VALID-TWO-MESSAGES): one canonical wording per rule
|
|
54
|
-
// across BOTH validators. The request Validator says "does not match
|
|
55
|
-
// the required format"; the ORM validator must say the same so a
|
|
56
|
-
// client keying on the message matches either surface.
|
|
57
|
-
errors.push({ field: name, message: `does not match the required format` });
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
break;
|
|
61
|
-
|
|
62
|
-
case "integer":
|
|
63
|
-
case "number":
|
|
64
|
-
case "numeric": {
|
|
65
|
-
const num = typeof value === "string" ? Number(value) : value;
|
|
66
|
-
if (typeof num !== "number" || isNaN(num)) {
|
|
67
|
-
errors.push({ field: name, message: "must be a number" });
|
|
68
|
-
} else {
|
|
69
|
-
if (def.type === "integer" && !Number.isInteger(num)) {
|
|
70
|
-
errors.push({ field: name, message: "must be an integer" });
|
|
71
|
-
}
|
|
72
|
-
if (def.min !== undefined && num < def.min) {
|
|
73
|
-
errors.push({ field: name, message: `must be at least ${def.min}` });
|
|
74
|
-
}
|
|
75
|
-
if (def.max !== undefined && num > def.max) {
|
|
76
|
-
errors.push({ field: name, message: `must be at most ${def.max}` });
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
case "boolean":
|
|
83
|
-
if (typeof value !== "boolean" && value !== 0 && value !== 1 && value !== "true" && value !== "false") {
|
|
84
|
-
errors.push({ field: name, message: "must be a boolean" });
|
|
85
|
-
}
|
|
86
|
-
break;
|
|
87
|
-
|
|
88
|
-
case "datetime":
|
|
89
|
-
if (typeof value === "string" && isNaN(Date.parse(value))) {
|
|
90
|
-
errors.push({ field: name, message: "must be a valid date/time" });
|
|
91
|
-
}
|
|
92
|
-
break;
|
|
93
|
-
|
|
94
|
-
case "json":
|
|
95
|
-
// A JSON column holds an object/array (or a pre-serialised JSON
|
|
96
|
-
// string); reject a bare scalar. A value that can't be JSON-encoded
|
|
97
|
-
// (a circular reference, a BigInt) fails loud at save time.
|
|
98
|
-
if (value !== null && typeof value !== "object" && typeof value !== "string") {
|
|
99
|
-
errors.push({ field: name, message: "must be a JSON object or array" });
|
|
100
|
-
}
|
|
101
|
-
break;
|
|
102
|
-
|
|
103
|
-
case "foreignKey": {
|
|
104
|
-
// Outlier D: previously there was no foreignKey case, so ANY value
|
|
105
|
-
// passed validation silently. A foreign key references another model's
|
|
106
|
-
// primary key — by default an auto-increment integer — so validate it
|
|
107
|
-
// as an integer (a numeric string like "12" is coerced and accepted).
|
|
108
|
-
// This catches the common bug of assigning a whole object / array /
|
|
109
|
-
// non-numeric string to an *_id column before it reaches the driver.
|
|
110
|
-
const fkNum = typeof value === "string" ? Number(value) : value;
|
|
111
|
-
if (typeof fkNum !== "number" || isNaN(fkNum) || !Number.isInteger(fkNum)) {
|
|
112
|
-
errors.push({ field: name, message: "must be a valid foreign key (integer)" });
|
|
113
|
-
}
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
133
|
+
errors.push(...validateField(name, value, def, compiledPatterns.get(name)));
|
|
117
134
|
}
|
|
118
135
|
|
|
119
136
|
return errors;
|
|
@@ -146,6 +146,10 @@ export declare class Ai {
|
|
|
146
146
|
*/
|
|
147
147
|
private static validateMessages;
|
|
148
148
|
private static validateContent;
|
|
149
|
+
private static validateContentPart;
|
|
150
|
+
private static validateTextPart;
|
|
151
|
+
private static validateImagePart;
|
|
152
|
+
private static validateToolResultPart;
|
|
149
153
|
/**
|
|
150
154
|
* Validate the outbound tool declarations (ADR-0061). Each tool needs a
|
|
151
155
|
* non-empty `name`, a string `description`, and a JSON-Schema-shaped
|
|
@@ -215,5 +219,6 @@ export declare class Ai {
|
|
|
215
219
|
*/
|
|
216
220
|
private static streamRequest;
|
|
217
221
|
private static responseChunks;
|
|
222
|
+
private static readStream;
|
|
218
223
|
private static streamError;
|
|
219
224
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - System info (Node.js version, V8, memory, uptime, platform)
|
|
10
10
|
*/
|
|
11
11
|
import type { Router } from "./router.js";
|
|
12
|
-
import type { Tina4Request } from "./types.js";
|
|
12
|
+
import type { RouteHandler, Tina4Request } from "./types.js";
|
|
13
13
|
/** Safe HTTP methods that never carry a state change — they skip the write gate. */
|
|
14
14
|
export declare const DEV_SAFE_METHODS: Set<string>;
|
|
15
15
|
/**
|
|
@@ -203,6 +203,19 @@ export declare class DevAdmin {
|
|
|
203
203
|
* 4. Fallback `http://127.0.0.1:9145` — matches standalone `tina4 agent`.
|
|
204
204
|
*/
|
|
205
205
|
export declare function supervisorBaseUrl(): string;
|
|
206
|
+
/**
|
|
207
|
+
* Version check — a check that did not happen says so.
|
|
208
|
+
*
|
|
209
|
+
* This used to fall back to `latest = current` on any failure, and the toolbar
|
|
210
|
+
* renders that as a green "You are up to date!" — so a developer several
|
|
211
|
+
* releases behind, on a machine with no route out, was told the opposite of the
|
|
212
|
+
* truth, and the toolbar's own "Could not check for updates" branch could never
|
|
213
|
+
* fire because the failure arrived as a success. `latest` is `null` when the
|
|
214
|
+
* check could not be made, and `error` says why. The registry URL is
|
|
215
|
+
* `TINA4_VERSION_CHECK_URL` when set (a mirror, or a test's own server), else
|
|
216
|
+
* npm. Mirrors Python `tina4_python.dev_admin._api_version_check`.
|
|
217
|
+
*/
|
|
218
|
+
export declare const handleVersionCheck: RouteHandler;
|
|
206
219
|
/**
|
|
207
220
|
* Resolve a CodeMirror-friendly language id from a file path's basename.
|
|
208
221
|
*
|
|
@@ -212,4 +225,13 @@ export declare function supervisorBaseUrl(): string;
|
|
|
212
225
|
* - anything unknown → "text"
|
|
213
226
|
*/
|
|
214
227
|
export declare function devAdminLanguage(rel: string): string;
|
|
228
|
+
/**
|
|
229
|
+
* JS for the injected dev toolbar — the version-check modal, the dashboard
|
|
230
|
+
* overlay, and the WebSocket-primary live reloader. Served as an external
|
|
231
|
+
* script so the toolbar carries no inline handlers or `<script>` and stays
|
|
232
|
+
* CSP-clean. Every interaction is wired via addEventListener. The reloader only
|
|
233
|
+
* starts when the toolbar's `data-reload` is "1" (reload not suppressed for this
|
|
234
|
+
* request/port). Mirrors PHP DevAdmin::toolbarJs().
|
|
235
|
+
*/
|
|
236
|
+
export declare function toolbarJs(): string;
|
|
215
237
|
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface ParsedClass {
|
|
2
|
+
name: string;
|
|
3
|
+
line: number;
|
|
4
|
+
doc: string;
|
|
5
|
+
exported: boolean;
|
|
6
|
+
methods: ParsedMethod[];
|
|
7
|
+
}
|
|
8
|
+
export interface ParsedMethod {
|
|
9
|
+
name: string;
|
|
10
|
+
line: number;
|
|
11
|
+
doc: string;
|
|
12
|
+
signature: string;
|
|
13
|
+
visibility: "public" | "protected" | "private";
|
|
14
|
+
static: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface ParsedFile {
|
|
17
|
+
classes: ParsedClass[];
|
|
18
|
+
functions: ParsedMethod[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse a TS source string. Lightweight — finds top-level classes and their
|
|
22
|
+
* public methods, plus top-level exported functions. Captures preceding JSDoc.
|
|
23
|
+
*
|
|
24
|
+
* Strategy: scan token-by-token. We don't need a full AST — we only care
|
|
25
|
+
* about identifying class declarations, brace depth (to find class members),
|
|
26
|
+
* method/function declarations, and JSDoc comments immediately above.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseTypeScript(source: string, _debugTag?: string): ParsedFile;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function stripStrings(source: string): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface MethodMatch {
|
|
2
|
+
name: string;
|
|
3
|
+
signature: string;
|
|
4
|
+
endIndex: number;
|
|
5
|
+
nameStart: number;
|
|
6
|
+
visibility: "public" | "protected" | "private";
|
|
7
|
+
static: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function matchMethodSignature(stripped: string, source: string, i: number): MethodMatch | null;
|
|
10
|
+
export declare function matchTopLevelFunction(stripped: string, source: string, i: number): MethodMatch | null;
|
|
11
|
+
export {};
|
|
@@ -57,6 +57,8 @@ export { AI_TOOLS, isInstalled, showMenu, installSelected, installAll, generateC
|
|
|
57
57
|
export type { AiTool } from "./ai.js";
|
|
58
58
|
export { Sso, SSO, SsoError } from "./sso.js";
|
|
59
59
|
export type { SsoOptions } from "./sso.js";
|
|
60
|
+
export { Push, PushError, generateVapidKeys } from "./push.js";
|
|
61
|
+
export type { PushOptions, PushSubscription, PushResult, PushPayload } from "./push.js";
|
|
60
62
|
export { Ai, AiError, AiConfigError, AiHTTPError, AiTimeoutError, AiParseError } from "./aiClient.js";
|
|
61
63
|
export type { ChatResponse, AiMessage, AiChatOptions, AiEmbedOptions, AiEvent, ContentPart, AiMessageContent, AiToolDeclaration, AiToolChoice } from "./aiClient.js";
|
|
62
64
|
export type { ImapMessage, ImapFullMessage, ImapAttachment } from "./messenger.js";
|
|
@@ -139,6 +139,14 @@ export declare class Messenger {
|
|
|
139
139
|
private shouldCapture;
|
|
140
140
|
/** The local mailbox, created on first capture and reused after. */
|
|
141
141
|
private getDevMailbox;
|
|
142
|
+
private prepareRecipients;
|
|
143
|
+
private connectSmtpSocket;
|
|
144
|
+
private requireSmtpResponse;
|
|
145
|
+
private openSmtpSession;
|
|
146
|
+
private authenticateSmtp;
|
|
147
|
+
private sendSmtpEnvelope;
|
|
148
|
+
private sendSmtpMessage;
|
|
149
|
+
private sendSmtp;
|
|
142
150
|
send(to: string | string[], subject: string, body: string, html?: boolean, text?: string, cc?: string | string[], bcc?: string | string[], replyTo?: string, attachments?: string[], headers?: Record<string, string>): Promise<SendResult>;
|
|
143
151
|
/**
|
|
144
152
|
* Render a Frond template STRING and send it as an HTML email (G7, parity with
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FileEntry } from "./projectIndex.js";
|
|
2
|
+
export interface IndexData {
|
|
3
|
+
version: number;
|
|
4
|
+
files: Record<string, FileEntry>;
|
|
5
|
+
generated_at: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function projectRoot(): string;
|
|
8
|
+
export declare function indexPath(): string;
|
|
9
|
+
export declare function summarise(entry: FileEntry): string;
|
|
10
|
+
export declare function extract(fullPath: string): FileEntry;
|
|
11
|
+
export declare function walk(dir: string, out: string[]): void;
|
|
12
|
+
export declare function loadRaw(): IndexData;
|
|
13
|
+
export declare function saveRaw(data: IndexData): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface PushSubscription {
|
|
2
|
+
endpoint: string;
|
|
3
|
+
keys: {
|
|
4
|
+
p256dh: string;
|
|
5
|
+
auth: string;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export interface PushOptions {
|
|
9
|
+
subject?: string;
|
|
10
|
+
publicKey?: string;
|
|
11
|
+
privateKey?: string;
|
|
12
|
+
ttl?: number;
|
|
13
|
+
urgency?: "very-low" | "low" | "normal" | "high";
|
|
14
|
+
}
|
|
15
|
+
export interface PushResult {
|
|
16
|
+
ok: boolean;
|
|
17
|
+
status: number;
|
|
18
|
+
dead: boolean;
|
|
19
|
+
retryable: boolean;
|
|
20
|
+
endpoint: string;
|
|
21
|
+
response: string;
|
|
22
|
+
}
|
|
23
|
+
export type PushPayload = string | Uint8Array | Record<string, unknown> | unknown[] | number | boolean | null;
|
|
24
|
+
export declare class PushError extends Error {
|
|
25
|
+
constructor(message: string);
|
|
26
|
+
}
|
|
27
|
+
export declare function generateVapidKeys(): {
|
|
28
|
+
publicKey: string;
|
|
29
|
+
privateKey: string;
|
|
30
|
+
};
|
|
31
|
+
/** Provider-neutral Web Push sender. A subscription is accepted as returned by PushManager. */
|
|
32
|
+
export declare class Push {
|
|
33
|
+
private readonly options;
|
|
34
|
+
constructor(options?: PushOptions);
|
|
35
|
+
static fromEnv(options?: PushOptions): Push;
|
|
36
|
+
static generateKeys(): {
|
|
37
|
+
publicKey: string;
|
|
38
|
+
privateKey: string;
|
|
39
|
+
};
|
|
40
|
+
private requireConfiguration;
|
|
41
|
+
private endpointFor;
|
|
42
|
+
private vapidKeys;
|
|
43
|
+
private deliver;
|
|
44
|
+
send(subscription: PushSubscription, payload: PushPayload): Promise<PushResult>;
|
|
45
|
+
}
|
|
@@ -97,6 +97,7 @@ export declare class Frond {
|
|
|
97
97
|
private _allowedVars;
|
|
98
98
|
private fragmentCache;
|
|
99
99
|
private _autoEscape;
|
|
100
|
+
private readonly blockHandlers;
|
|
100
101
|
/**
|
|
101
102
|
* Token pre-compilation cache for file templates.
|
|
102
103
|
*
|
|
@@ -188,6 +189,10 @@ export declare class Frond {
|
|
|
188
189
|
*/
|
|
189
190
|
private substituteBlocks;
|
|
190
191
|
private renderWithBlocks;
|
|
192
|
+
private dispatchBlock;
|
|
193
|
+
private renderTextToken;
|
|
194
|
+
private renderVarToken;
|
|
195
|
+
private renderBlockToken;
|
|
191
196
|
private renderTokens;
|
|
192
197
|
/**
|
|
193
198
|
* May this filter RUN under the current sandbox?
|
|
@@ -206,6 +211,7 @@ export declare class Frond {
|
|
|
206
211
|
* instead of the four names that happened to be checked individually.
|
|
207
212
|
*/
|
|
208
213
|
private tagPermitted;
|
|
214
|
+
private applyFilterValue;
|
|
209
215
|
/**
|
|
210
216
|
* Consume a denied tag WITHOUT running it, returning the index past its body.
|
|
211
217
|
*
|
|
@@ -228,8 +234,22 @@ export declare class Frond {
|
|
|
228
234
|
private applyFilters;
|
|
229
235
|
private evalVar;
|
|
230
236
|
private evalVarRaw;
|
|
237
|
+
/**
|
|
238
|
+
* Apply the no-argument filters that are common enough to avoid generic
|
|
239
|
+
* dispatch. Keeping this table separate from evalVarInner makes the
|
|
240
|
+
* expression pipeline easier to audit without changing filter order.
|
|
241
|
+
*/
|
|
242
|
+
private applyFastFilter;
|
|
243
|
+
private applyRenderedFilter;
|
|
244
|
+
private variablePermitted;
|
|
245
|
+
private resolveConcatenation;
|
|
246
|
+
private applyRenderedFilters;
|
|
231
247
|
private evalVarInner;
|
|
248
|
+
private pushIfBranch;
|
|
249
|
+
private collectIfBranches;
|
|
232
250
|
private handleIf;
|
|
251
|
+
private collectForTokens;
|
|
252
|
+
private forItems;
|
|
233
253
|
private handleFor;
|
|
234
254
|
private handleSet;
|
|
235
255
|
private handleInclude;
|
|
@@ -257,6 +277,8 @@ export declare class Frond {
|
|
|
257
277
|
*/
|
|
258
278
|
private handleImportAs;
|
|
259
279
|
private handleFromImport;
|
|
280
|
+
private collectMacroDefinitions;
|
|
281
|
+
private createMacro;
|
|
260
282
|
/**
|
|
261
283
|
* Collect the body tokens of a {% <openTag> %}...{% end<openTag> %} block,
|
|
262
284
|
* starting from the token after the opening tag (start + 1). Nested same-tag
|
|
@@ -279,6 +301,9 @@ export declare class Frond {
|
|
|
279
301
|
* Python master's _handle_live and PHP/Ruby handleLive.
|
|
280
302
|
*/
|
|
281
303
|
private handleLive;
|
|
304
|
+
private parseLiveOptions;
|
|
305
|
+
private collectLiveBody;
|
|
306
|
+
private liveAttributes;
|
|
282
307
|
/**
|
|
283
308
|
* Re-render a registered {% live %} fragment by name with fresh data.
|
|
284
309
|
* Returns the rendered HTML, or null if no fragment is registered under that
|
|
@@ -19,6 +19,9 @@ export declare class FakeData extends CoreFakeData {
|
|
|
19
19
|
* Matches the Python API's datetime() method.
|
|
20
20
|
*/
|
|
21
21
|
datetime(startYear?: number, endYear?: number): Date;
|
|
22
|
+
private heuristicValue;
|
|
23
|
+
private stringValue;
|
|
24
|
+
private typedValue;
|
|
22
25
|
/**
|
|
23
26
|
* Generate a fake value appropriate for an ORM field definition.
|
|
24
27
|
* Respects min/max, minLength/maxLength, and type constraints.
|