@vurb/core 3.3.4 → 3.6.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/README.md +726 -677
- package/dist/cli/constants.js +59 -59
- package/dist/cli/scaffold.js +2 -0
- package/dist/cli/scaffold.js.map +1 -1
- package/dist/cli/templates/config.js +27 -27
- package/dist/cli/templates/config.js.map +1 -1
- package/dist/cli/templates/core.js +95 -95
- package/dist/cli/templates/index.d.ts +1 -0
- package/dist/cli/templates/index.d.ts.map +1 -1
- package/dist/cli/templates/index.js +2 -0
- package/dist/cli/templates/index.js.map +1 -1
- package/dist/cli/templates/middleware.js +25 -25
- package/dist/cli/templates/model.d.ts +7 -0
- package/dist/cli/templates/model.d.ts.map +1 -0
- package/dist/cli/templates/model.js +31 -0
- package/dist/cli/templates/model.js.map +1 -0
- package/dist/cli/templates/presenter.d.ts.map +1 -1
- package/dist/cli/templates/presenter.js +4 -9
- package/dist/cli/templates/presenter.js.map +1 -1
- package/dist/cli/templates/readme.d.ts.map +1 -1
- package/dist/cli/templates/readme.js +144 -142
- package/dist/cli/templates/readme.js.map +1 -1
- package/dist/cli/templates/testing.js +84 -84
- package/dist/cli/templates/tools.js +46 -46
- package/dist/cli/templates/vectors/database.js +69 -69
- package/dist/cli/templates/vectors/oauth.js +63 -63
- package/dist/cli/templates/vectors/openapi.js +97 -97
- package/dist/core/builder/BuildPipeline.d.ts +56 -0
- package/dist/core/builder/BuildPipeline.d.ts.map +1 -0
- package/dist/core/builder/BuildPipeline.js +139 -0
- package/dist/core/builder/BuildPipeline.js.map +1 -0
- package/dist/core/builder/FluentToolBuilder.d.ts +253 -19
- package/dist/core/builder/FluentToolBuilder.d.ts.map +1 -1
- package/dist/core/builder/FluentToolBuilder.js +354 -114
- package/dist/core/builder/FluentToolBuilder.js.map +1 -1
- package/dist/core/builder/ProxyHandler.d.ts +26 -0
- package/dist/core/builder/ProxyHandler.d.ts.map +1 -0
- package/dist/core/builder/ProxyHandler.js +65 -0
- package/dist/core/builder/ProxyHandler.js.map +1 -0
- package/dist/core/builder/SemanticDefaults.d.ts +25 -0
- package/dist/core/builder/SemanticDefaults.d.ts.map +1 -0
- package/dist/core/builder/SemanticDefaults.js +16 -0
- package/dist/core/builder/SemanticDefaults.js.map +1 -0
- package/dist/core/builder/index.d.ts +2 -1
- package/dist/core/builder/index.d.ts.map +1 -1
- package/dist/core/builder/index.js +1 -0
- package/dist/core/builder/index.js.map +1 -1
- package/dist/core/middleware/AuditTrail.d.ts.map +1 -1
- package/dist/core/middleware/AuditTrail.js +6 -2
- package/dist/core/middleware/AuditTrail.js.map +1 -1
- package/dist/core/middleware/RateLimiter.d.ts.map +1 -1
- package/dist/core/middleware/RateLimiter.js +3 -1
- package/dist/core/middleware/RateLimiter.js.map +1 -1
- package/dist/fsm/StateMachineGate.d.ts +3 -0
- package/dist/fsm/StateMachineGate.d.ts.map +1 -1
- package/dist/fsm/StateMachineGate.js +12 -0
- package/dist/fsm/StateMachineGate.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/introspection/SemanticProbe.js +49 -49
- package/dist/model/defineModel.d.ts +167 -0
- package/dist/model/defineModel.d.ts.map +1 -0
- package/dist/model/defineModel.js +345 -0
- package/dist/model/defineModel.js.map +1 -0
- package/dist/model/index.d.ts +8 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +7 -0
- package/dist/model/index.js.map +1 -0
- package/dist/observability/TelemetryBus.d.ts.map +1 -1
- package/dist/observability/TelemetryBus.js +14 -13
- package/dist/observability/TelemetryBus.js.map +1 -1
- package/dist/presenter/Presenter.d.ts +21 -0
- package/dist/presenter/Presenter.d.ts.map +1 -1
- package/dist/presenter/Presenter.js +17 -8
- package/dist/presenter/Presenter.js.map +1 -1
- package/dist/presenter/PresenterPipeline.d.ts +2 -0
- package/dist/presenter/PresenterPipeline.d.ts.map +1 -1
- package/dist/presenter/PresenterPipeline.js +4 -0
- package/dist/presenter/PresenterPipeline.js.map +1 -1
- package/dist/presenter/definePresenter.d.ts.map +1 -1
- package/dist/presenter/definePresenter.js +13 -3
- package/dist/presenter/definePresenter.js.map +1 -1
- package/dist/prompt/FluentPromptBuilder.d.ts.map +1 -1
- package/dist/resource/ResourceRegistry.d.ts +3 -0
- package/dist/resource/ResourceRegistry.d.ts.map +1 -1
- package/dist/resource/ResourceRegistry.js +8 -2
- package/dist/resource/ResourceRegistry.js.map +1 -1
- package/dist/sandbox/SandboxGuard.js +7 -4
- package/dist/sandbox/SandboxGuard.js.map +1 -1
- package/dist/server/ServerAttachment.d.ts.map +1 -1
- package/dist/server/ServerAttachment.js +48 -9
- package/dist/server/ServerAttachment.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineModel — Eloquent-inspired Model Definition for Vurb.ts
|
|
3
|
+
*
|
|
4
|
+
* Synthesizes the best DX patterns from 10 globally acclaimed frameworks:
|
|
5
|
+
* • Laravel: $casts, $fillable, $hidden, $guarded, timestamps()
|
|
6
|
+
* • Django: choices (enum), verbose_name (label-first)
|
|
7
|
+
* • Rails: attr_accessible (whitelist) + attr_protected (blacklist)
|
|
8
|
+
* • Prisma: @default() values
|
|
9
|
+
* • Pydantic: Field(alias=, examples=, description=)
|
|
10
|
+
* • Drizzle: column-as-function type helpers
|
|
11
|
+
* • Convex: defineTable() factory
|
|
12
|
+
* • Spring: @JsonIgnore → hidden
|
|
13
|
+
* • NestJS: DTOs per operation → fillable profiles
|
|
14
|
+
* • Mongoose: schema options in closure
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { defineModel } from '@vurb/core';
|
|
19
|
+
*
|
|
20
|
+
* export const TaskModel = defineModel('Task', m => {
|
|
21
|
+
* m.casts({
|
|
22
|
+
* title: m.string('Task title'),
|
|
23
|
+
* description: m.text('Description in markdown'),
|
|
24
|
+
* status: m.enum('Status', ['open', 'done']).default('open'),
|
|
25
|
+
* due_date: m.date('Due date'),
|
|
26
|
+
* is_bug: m.boolean('Bug flag').default(false),
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* m.timestamps();
|
|
30
|
+
* m.hidden(['internal_id']);
|
|
31
|
+
* m.guarded(['uuid', 'created_at', 'updated_at']);
|
|
32
|
+
*
|
|
33
|
+
* m.fillable({
|
|
34
|
+
* create: ['title', 'description', 'due_date', 'is_bug'],
|
|
35
|
+
* update: ['title', 'description', 'due_date', 'is_bug'],
|
|
36
|
+
* filter: ['title', 'status', 'is_bug'],
|
|
37
|
+
* });
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @module
|
|
42
|
+
*/
|
|
43
|
+
import { z, type ZodType, type ZodObject, type ZodRawShape } from 'zod';
|
|
44
|
+
/** Internal field type discriminator */
|
|
45
|
+
type FieldType = 'string' | 'text' | 'number' | 'boolean' | 'date' | 'timestamp' | 'uuid' | 'id' | 'enum' | 'object' | 'list';
|
|
46
|
+
/** A field definition descriptor — chainable for .default(), .alias(), .examples() */
|
|
47
|
+
export declare class FieldDef {
|
|
48
|
+
/** @internal */ readonly _type: FieldType;
|
|
49
|
+
/** @internal */ readonly _label: string | undefined;
|
|
50
|
+
/** @internal */ readonly _enumValues: readonly [string, ...string[]] | undefined;
|
|
51
|
+
/** @internal */ readonly _shape: Record<string, FieldDef> | undefined;
|
|
52
|
+
/** @internal */ _defaultValue?: unknown;
|
|
53
|
+
/** @internal */ _alias?: string;
|
|
54
|
+
/** @internal */ _examples?: unknown[];
|
|
55
|
+
constructor(type: FieldType, label?: string, options?: {
|
|
56
|
+
enumValues?: readonly [string, ...string[]];
|
|
57
|
+
shape?: Record<string, FieldDef>;
|
|
58
|
+
});
|
|
59
|
+
/** Set a default value (Prisma-inspired `@default()`) */
|
|
60
|
+
default(value: unknown): this;
|
|
61
|
+
/** Set an alias name (Pydantic-inspired `Field(alias=)`) */
|
|
62
|
+
alias(name: string): this;
|
|
63
|
+
/** Set example values (Pydantic-inspired `Field(examples=[])`) */
|
|
64
|
+
examples(values: unknown[]): this;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Compile a FieldDef into a Zod schema for input context.
|
|
68
|
+
*
|
|
69
|
+
* Unlike `compileField()` (which defaults everything to optional for output schemas),
|
|
70
|
+
* this function produces schemas suitable for tool input parameters:
|
|
71
|
+
* - For **create**: string/text fields become required `z.string()`, number fields become optional
|
|
72
|
+
* - For **update/filter** (`forceOptional = true`): all fields become optional
|
|
73
|
+
*
|
|
74
|
+
* @param def - Field definition to compile
|
|
75
|
+
* @param forceOptional - If true, all fields become optional (update/filter semantics)
|
|
76
|
+
* @returns Zod schema for input validation
|
|
77
|
+
*/
|
|
78
|
+
export declare function compileFieldForInput(def: FieldDef, forceOptional: boolean): ZodType;
|
|
79
|
+
/** The compiled Model object returned by `defineModel()` */
|
|
80
|
+
export interface Model {
|
|
81
|
+
/** Model name (e.g. 'Task') */
|
|
82
|
+
readonly name: string;
|
|
83
|
+
/** Compiled Zod schema — compatible with Presenter.schema() */
|
|
84
|
+
readonly schema: ZodObject<ZodRawShape>;
|
|
85
|
+
/** Field definitions map */
|
|
86
|
+
readonly fields: Record<string, FieldDef>;
|
|
87
|
+
/** Hidden fields — never shown in output (Spring @JsonIgnore) */
|
|
88
|
+
readonly hidden: string[];
|
|
89
|
+
/** Guarded fields — never fillable (Rails attr_protected) */
|
|
90
|
+
readonly guarded: string[];
|
|
91
|
+
/** Input profiles per operation (Laravel $fillable) */
|
|
92
|
+
readonly input: Record<string, string[]>;
|
|
93
|
+
/** Default values (Prisma @default) */
|
|
94
|
+
readonly defaults: Record<string, unknown>;
|
|
95
|
+
/**
|
|
96
|
+
* Apply field aliases — renames agent-facing keys to API-facing keys.
|
|
97
|
+
* Pydantic-inspired `model_dump(by_alias=True)`.
|
|
98
|
+
* Strips undefined values. Non-aliased keys pass through unchanged.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Model: content → .alias('description')
|
|
103
|
+
* ProposalModel.toApi({ title: 'X', content: 'Y' })
|
|
104
|
+
* // → { title: 'X', description: 'Y' }
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
readonly toApi: (data: Record<string, unknown>) => Record<string, unknown>;
|
|
108
|
+
/** Type helper — `typeof model.infer` gives you the TS type */
|
|
109
|
+
readonly infer: z.infer<ZodObject<ZodRawShape>>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Builder class that exposes type functions and configuration methods.
|
|
113
|
+
* Passed as `m` in the `defineModel()` closure.
|
|
114
|
+
*/
|
|
115
|
+
export declare class ModelBuilder {
|
|
116
|
+
/** @internal */ _fields: Record<string, FieldDef>;
|
|
117
|
+
/** @internal */ _hidden: string[];
|
|
118
|
+
/** @internal */ _guarded: string[];
|
|
119
|
+
/** @internal */ _fillableProfiles: Record<string, string[]>;
|
|
120
|
+
/** String field — general purpose text */
|
|
121
|
+
string(label?: string): FieldDef;
|
|
122
|
+
/** Text field — semantic: markdown, multiline, long content */
|
|
123
|
+
text(label?: string): FieldDef;
|
|
124
|
+
/** Numeric field */
|
|
125
|
+
number(label?: string): FieldDef;
|
|
126
|
+
/** Boolean field */
|
|
127
|
+
boolean(label?: string): FieldDef;
|
|
128
|
+
/** Date field — automatically hints YYYY-MM-DD format */
|
|
129
|
+
date(label?: string): FieldDef;
|
|
130
|
+
/** Timestamp field — ISO datetime */
|
|
131
|
+
timestamp(label?: string): FieldDef;
|
|
132
|
+
/** UUID field — string with UUID semantics */
|
|
133
|
+
uuid(label?: string): FieldDef;
|
|
134
|
+
/** ID field — always required, numeric */
|
|
135
|
+
id(label?: string): FieldDef;
|
|
136
|
+
/** Enum field — Django-style choices, tells AI valid values */
|
|
137
|
+
enum(label: string, values: readonly [string, ...string[]]): FieldDef;
|
|
138
|
+
/** Nested object field */
|
|
139
|
+
object(label: string, shape: Record<string, FieldDef>): FieldDef;
|
|
140
|
+
/** Array of objects field */
|
|
141
|
+
list(label: string, shape: Record<string, FieldDef>): FieldDef;
|
|
142
|
+
/** Define field types and labels — like Laravel's `$casts` */
|
|
143
|
+
casts(fields: Record<string, FieldDef>): void;
|
|
144
|
+
/** Add created_at + updated_at timestamp fields — like Laravel's `timestamps()` */
|
|
145
|
+
timestamps(): void;
|
|
146
|
+
/** Fields hidden from output — like Spring's `@JsonIgnore` or Laravel's `$hidden` */
|
|
147
|
+
hidden(fields: string[]): void;
|
|
148
|
+
/** Fields that can NEVER be input — like Rails' `attr_protected` or Laravel's `$guarded` */
|
|
149
|
+
guarded(fields: string[]): void;
|
|
150
|
+
/** Define input profiles per operation — like Laravel's `$fillable` + NestJS DTOs */
|
|
151
|
+
fillable(profiles: Record<string, string[]>): void;
|
|
152
|
+
/** @internal Compile all fields into a Zod schema */
|
|
153
|
+
_compile(): {
|
|
154
|
+
schema: ZodObject<ZodRawShape>;
|
|
155
|
+
defaults: Record<string, unknown>;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Define a Model — one file, one entity, one source of truth.
|
|
160
|
+
*
|
|
161
|
+
* @param name - Model name (e.g. 'Task', 'Sprint', 'Project')
|
|
162
|
+
* @param configure - Closure function that receives a `ModelBuilder` (`m`)
|
|
163
|
+
* @returns Compiled `Model` with `.schema`, `.fields`, `.input`, `.hidden`, `.guarded`
|
|
164
|
+
*/
|
|
165
|
+
export declare function defineModel(name: string, configure: (m: ModelBuilder) => void): Model;
|
|
166
|
+
export {};
|
|
167
|
+
//# sourceMappingURL=defineModel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineModel.d.ts","sourceRoot":"","sources":["../../src/model/defineModel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineModel — Eloquent-inspired Model Definition for Vurb.ts
|
|
3
|
+
*
|
|
4
|
+
* Synthesizes the best DX patterns from 10 globally acclaimed frameworks:
|
|
5
|
+
* • Laravel: $casts, $fillable, $hidden, $guarded, timestamps()
|
|
6
|
+
* • Django: choices (enum), verbose_name (label-first)
|
|
7
|
+
* • Rails: attr_accessible (whitelist) + attr_protected (blacklist)
|
|
8
|
+
* • Prisma: @default() values
|
|
9
|
+
* • Pydantic: Field(alias=, examples=, description=)
|
|
10
|
+
* • Drizzle: column-as-function type helpers
|
|
11
|
+
* • Convex: defineTable() factory
|
|
12
|
+
* • Spring: @JsonIgnore → hidden
|
|
13
|
+
* • NestJS: DTOs per operation → fillable profiles
|
|
14
|
+
* • Mongoose: schema options in closure
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { defineModel } from '@vurb/core';
|
|
19
|
+
*
|
|
20
|
+
* export const TaskModel = defineModel('Task', m => {
|
|
21
|
+
* m.casts({
|
|
22
|
+
* title: m.string('Task title'),
|
|
23
|
+
* description: m.text('Description in markdown'),
|
|
24
|
+
* status: m.enum('Status', ['open', 'done']).default('open'),
|
|
25
|
+
* due_date: m.date('Due date'),
|
|
26
|
+
* is_bug: m.boolean('Bug flag').default(false),
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* m.timestamps();
|
|
30
|
+
* m.hidden(['internal_id']);
|
|
31
|
+
* m.guarded(['uuid', 'created_at', 'updated_at']);
|
|
32
|
+
*
|
|
33
|
+
* m.fillable({
|
|
34
|
+
* create: ['title', 'description', 'due_date', 'is_bug'],
|
|
35
|
+
* update: ['title', 'description', 'due_date', 'is_bug'],
|
|
36
|
+
* filter: ['title', 'status', 'is_bug'],
|
|
37
|
+
* });
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @module
|
|
42
|
+
*/
|
|
43
|
+
import { z } from 'zod';
|
|
44
|
+
/** A field definition descriptor — chainable for .default(), .alias(), .examples() */
|
|
45
|
+
export class FieldDef {
|
|
46
|
+
/** @internal */ _type;
|
|
47
|
+
/** @internal */ _label;
|
|
48
|
+
/** @internal */ _enumValues;
|
|
49
|
+
/** @internal */ _shape;
|
|
50
|
+
/** @internal */ _defaultValue;
|
|
51
|
+
/** @internal */ _alias;
|
|
52
|
+
/** @internal */ _examples;
|
|
53
|
+
constructor(type, label, options) {
|
|
54
|
+
this._type = type;
|
|
55
|
+
this._label = label;
|
|
56
|
+
this._enumValues = options?.enumValues;
|
|
57
|
+
this._shape = options?.shape;
|
|
58
|
+
}
|
|
59
|
+
/** Set a default value (Prisma-inspired `@default()`) */
|
|
60
|
+
default(value) {
|
|
61
|
+
this._defaultValue = value;
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/** Set an alias name (Pydantic-inspired `Field(alias=)`) */
|
|
65
|
+
alias(name) {
|
|
66
|
+
this._alias = name;
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
/** Set example values (Pydantic-inspired `Field(examples=[])`) */
|
|
70
|
+
examples(values) {
|
|
71
|
+
this._examples = values;
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// ── Compile FieldDef → Zod ──────────────────────────────
|
|
76
|
+
function compileField(def) {
|
|
77
|
+
let schema;
|
|
78
|
+
switch (def._type) {
|
|
79
|
+
case 'string':
|
|
80
|
+
case 'text':
|
|
81
|
+
schema = z.string();
|
|
82
|
+
break;
|
|
83
|
+
case 'number':
|
|
84
|
+
schema = z.number();
|
|
85
|
+
break;
|
|
86
|
+
case 'boolean':
|
|
87
|
+
schema = z.boolean();
|
|
88
|
+
break;
|
|
89
|
+
case 'date':
|
|
90
|
+
schema = z.string();
|
|
91
|
+
break;
|
|
92
|
+
case 'timestamp':
|
|
93
|
+
schema = z.string();
|
|
94
|
+
break;
|
|
95
|
+
case 'uuid':
|
|
96
|
+
schema = z.string();
|
|
97
|
+
break;
|
|
98
|
+
case 'id':
|
|
99
|
+
// IDs are always required — return early with describe if present
|
|
100
|
+
schema = z.number();
|
|
101
|
+
if (def._label)
|
|
102
|
+
schema = schema.describe(def._label);
|
|
103
|
+
return schema;
|
|
104
|
+
case 'enum':
|
|
105
|
+
schema = z.enum(def._enumValues);
|
|
106
|
+
break;
|
|
107
|
+
case 'object': {
|
|
108
|
+
const shape = {};
|
|
109
|
+
if (def._shape) {
|
|
110
|
+
for (const [key, childDef] of Object.entries(def._shape)) {
|
|
111
|
+
shape[key] = compileField(childDef);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
schema = z.object(shape);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case 'list': {
|
|
118
|
+
const itemShape = {};
|
|
119
|
+
if (def._shape) {
|
|
120
|
+
for (const [key, childDef] of Object.entries(def._shape)) {
|
|
121
|
+
itemShape[key] = compileField(childDef);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
schema = z.array(z.object(itemShape));
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Add description (label) — Drizzle + Django verbose_name
|
|
129
|
+
if (def._label) {
|
|
130
|
+
// Add format hint for date fields — automatically hint YYYY-MM-DD
|
|
131
|
+
const label = def._type === 'date'
|
|
132
|
+
? `${def._label} (YYYY-MM-DD)`
|
|
133
|
+
: def._label;
|
|
134
|
+
schema = schema.describe(label);
|
|
135
|
+
}
|
|
136
|
+
// Everything except `id` is optional + nullable by default
|
|
137
|
+
// (API responses may omit fields)
|
|
138
|
+
if (def._type === 'boolean' || def._type === 'list') {
|
|
139
|
+
schema = schema.optional();
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
schema = schema.optional().nullable();
|
|
143
|
+
}
|
|
144
|
+
return schema;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Compile a FieldDef into a Zod schema for input context.
|
|
148
|
+
*
|
|
149
|
+
* Unlike `compileField()` (which defaults everything to optional for output schemas),
|
|
150
|
+
* this function produces schemas suitable for tool input parameters:
|
|
151
|
+
* - For **create**: string/text fields become required `z.string()`, number fields become optional
|
|
152
|
+
* - For **update/filter** (`forceOptional = true`): all fields become optional
|
|
153
|
+
*
|
|
154
|
+
* @param def - Field definition to compile
|
|
155
|
+
* @param forceOptional - If true, all fields become optional (update/filter semantics)
|
|
156
|
+
* @returns Zod schema for input validation
|
|
157
|
+
*/
|
|
158
|
+
export function compileFieldForInput(def, forceOptional) {
|
|
159
|
+
let schema;
|
|
160
|
+
switch (def._type) {
|
|
161
|
+
case 'string':
|
|
162
|
+
case 'text':
|
|
163
|
+
case 'uuid':
|
|
164
|
+
case 'date':
|
|
165
|
+
case 'timestamp':
|
|
166
|
+
schema = z.string();
|
|
167
|
+
break;
|
|
168
|
+
case 'number':
|
|
169
|
+
schema = z.number();
|
|
170
|
+
break;
|
|
171
|
+
case 'boolean':
|
|
172
|
+
schema = z.boolean();
|
|
173
|
+
break;
|
|
174
|
+
case 'id':
|
|
175
|
+
schema = z.number();
|
|
176
|
+
break;
|
|
177
|
+
case 'enum':
|
|
178
|
+
schema = z.enum(def._enumValues);
|
|
179
|
+
break;
|
|
180
|
+
case 'object': {
|
|
181
|
+
const shape = {};
|
|
182
|
+
if (def._shape) {
|
|
183
|
+
for (const [key, childDef] of Object.entries(def._shape)) {
|
|
184
|
+
shape[key] = compileFieldForInput(childDef, false);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
schema = z.object(shape);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case 'list': {
|
|
191
|
+
const itemShape = {};
|
|
192
|
+
if (def._shape) {
|
|
193
|
+
for (const [key, childDef] of Object.entries(def._shape)) {
|
|
194
|
+
itemShape[key] = compileFieldForInput(childDef, false);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
schema = z.array(z.object(itemShape));
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Add description (label)
|
|
202
|
+
if (def._label) {
|
|
203
|
+
const label = def._type === 'date'
|
|
204
|
+
? `${def._label} (YYYY-MM-DD)`
|
|
205
|
+
: def._label;
|
|
206
|
+
schema = schema.describe(label);
|
|
207
|
+
}
|
|
208
|
+
// Apply optionality: forceOptional makes everything optional
|
|
209
|
+
if (forceOptional) {
|
|
210
|
+
schema = schema.optional();
|
|
211
|
+
}
|
|
212
|
+
return schema;
|
|
213
|
+
}
|
|
214
|
+
// ── ModelBuilder (the `m` argument) ─────────────────────
|
|
215
|
+
/**
|
|
216
|
+
* Builder class that exposes type functions and configuration methods.
|
|
217
|
+
* Passed as `m` in the `defineModel()` closure.
|
|
218
|
+
*/
|
|
219
|
+
export class ModelBuilder {
|
|
220
|
+
/** @internal */ _fields = {};
|
|
221
|
+
/** @internal */ _hidden = [];
|
|
222
|
+
/** @internal */ _guarded = [];
|
|
223
|
+
/** @internal */ _fillableProfiles = {};
|
|
224
|
+
// ── Type Functions (Drizzle-style) ──────────────────
|
|
225
|
+
/** String field — general purpose text */
|
|
226
|
+
string(label) {
|
|
227
|
+
return new FieldDef('string', label);
|
|
228
|
+
}
|
|
229
|
+
/** Text field — semantic: markdown, multiline, long content */
|
|
230
|
+
text(label) {
|
|
231
|
+
return new FieldDef('text', label);
|
|
232
|
+
}
|
|
233
|
+
/** Numeric field */
|
|
234
|
+
number(label) {
|
|
235
|
+
return new FieldDef('number', label);
|
|
236
|
+
}
|
|
237
|
+
/** Boolean field */
|
|
238
|
+
boolean(label) {
|
|
239
|
+
return new FieldDef('boolean', label);
|
|
240
|
+
}
|
|
241
|
+
/** Date field — automatically hints YYYY-MM-DD format */
|
|
242
|
+
date(label) {
|
|
243
|
+
return new FieldDef('date', label);
|
|
244
|
+
}
|
|
245
|
+
/** Timestamp field — ISO datetime */
|
|
246
|
+
timestamp(label) {
|
|
247
|
+
return new FieldDef('timestamp', label);
|
|
248
|
+
}
|
|
249
|
+
/** UUID field — string with UUID semantics */
|
|
250
|
+
uuid(label) {
|
|
251
|
+
return new FieldDef('uuid', label);
|
|
252
|
+
}
|
|
253
|
+
/** ID field — always required, numeric */
|
|
254
|
+
id(label) {
|
|
255
|
+
return new FieldDef('id', label);
|
|
256
|
+
}
|
|
257
|
+
/** Enum field — Django-style choices, tells AI valid values */
|
|
258
|
+
enum(label, values) {
|
|
259
|
+
return new FieldDef('enum', label, { enumValues: values });
|
|
260
|
+
}
|
|
261
|
+
/** Nested object field */
|
|
262
|
+
object(label, shape) {
|
|
263
|
+
return new FieldDef('object', label, { shape });
|
|
264
|
+
}
|
|
265
|
+
/** Array of objects field */
|
|
266
|
+
list(label, shape) {
|
|
267
|
+
return new FieldDef('list', label, { shape });
|
|
268
|
+
}
|
|
269
|
+
// ── Configuration Methods (Laravel-style) ───────────
|
|
270
|
+
/** Define field types and labels — like Laravel's `$casts` */
|
|
271
|
+
casts(fields) {
|
|
272
|
+
this._fields = { ...this._fields, ...fields };
|
|
273
|
+
}
|
|
274
|
+
/** Add created_at + updated_at timestamp fields — like Laravel's `timestamps()` */
|
|
275
|
+
timestamps() {
|
|
276
|
+
this._fields['created_at'] = new FieldDef('timestamp', 'Creation timestamp');
|
|
277
|
+
this._fields['updated_at'] = new FieldDef('timestamp', 'Last update timestamp');
|
|
278
|
+
}
|
|
279
|
+
/** Fields hidden from output — like Spring's `@JsonIgnore` or Laravel's `$hidden` */
|
|
280
|
+
hidden(fields) {
|
|
281
|
+
this._hidden = fields;
|
|
282
|
+
}
|
|
283
|
+
/** Fields that can NEVER be input — like Rails' `attr_protected` or Laravel's `$guarded` */
|
|
284
|
+
guarded(fields) {
|
|
285
|
+
this._guarded = fields;
|
|
286
|
+
}
|
|
287
|
+
/** Define input profiles per operation — like Laravel's `$fillable` + NestJS DTOs */
|
|
288
|
+
fillable(profiles) {
|
|
289
|
+
this._fillableProfiles = profiles;
|
|
290
|
+
}
|
|
291
|
+
// ── Compile ─────────────────────────────────────────
|
|
292
|
+
/** @internal Compile all fields into a Zod schema */
|
|
293
|
+
_compile() {
|
|
294
|
+
const shape = {};
|
|
295
|
+
const defaults = {};
|
|
296
|
+
for (const [name, def] of Object.entries(this._fields)) {
|
|
297
|
+
shape[name] = compileField(def);
|
|
298
|
+
if (def._defaultValue !== undefined) {
|
|
299
|
+
defaults[name] = def._defaultValue;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return { schema: z.object(shape), defaults };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// ── Factory ─────────────────────────────────────────────
|
|
306
|
+
/**
|
|
307
|
+
* Define a Model — one file, one entity, one source of truth.
|
|
308
|
+
*
|
|
309
|
+
* @param name - Model name (e.g. 'Task', 'Sprint', 'Project')
|
|
310
|
+
* @param configure - Closure function that receives a `ModelBuilder` (`m`)
|
|
311
|
+
* @returns Compiled `Model` with `.schema`, `.fields`, `.input`, `.hidden`, `.guarded`
|
|
312
|
+
*/
|
|
313
|
+
export function defineModel(name, configure) {
|
|
314
|
+
const builder = new ModelBuilder();
|
|
315
|
+
configure(builder);
|
|
316
|
+
const { schema, defaults } = builder._compile();
|
|
317
|
+
// Pre-compute alias map at definition time (not per-call)
|
|
318
|
+
const aliasMap = new Map();
|
|
319
|
+
for (const [fieldName, fieldDef] of Object.entries(builder._fields)) {
|
|
320
|
+
if (fieldDef._alias) {
|
|
321
|
+
aliasMap.set(fieldName, fieldDef._alias);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const toApi = (data) => {
|
|
325
|
+
const result = {};
|
|
326
|
+
for (const [k, v] of Object.entries(data)) {
|
|
327
|
+
if (v !== undefined) {
|
|
328
|
+
result[aliasMap.get(k) ?? k] = v;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return result;
|
|
332
|
+
};
|
|
333
|
+
return Object.freeze({
|
|
334
|
+
name,
|
|
335
|
+
schema,
|
|
336
|
+
fields: Object.freeze(builder._fields),
|
|
337
|
+
hidden: Object.freeze(builder._hidden),
|
|
338
|
+
guarded: Object.freeze(builder._guarded),
|
|
339
|
+
input: Object.freeze(builder._fillableProfiles),
|
|
340
|
+
defaults: Object.freeze(defaults),
|
|
341
|
+
toApi,
|
|
342
|
+
infer: undefined,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=defineModel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineModel.js","sourceRoot":"","sources":["../../src/model/defineModel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/model/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7F,YAAY,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/model/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TelemetryBus.d.ts","sourceRoot":"","sources":["../../src/observability/TelemetryBus.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAkCzE;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAM7D;AA2DD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmDpF;AA2ED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,cAAc,GAAG,SAAS,CAAC;CACzD;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAC;IACnC,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"TelemetryBus.d.ts","sourceRoot":"","sources":["../../src/observability/TelemetryBus.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAkCzE;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAM7D;AA2DD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmDpF;AA2ED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,cAAc,GAAG,SAAS,CAAC;CACzD;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAC;IACnC,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAsKnG"}
|
|
@@ -326,6 +326,18 @@ export async function createTelemetryBus(config) {
|
|
|
326
326
|
});
|
|
327
327
|
});
|
|
328
328
|
// ── Start Listening ───────────────────────────────────
|
|
329
|
+
// Bug #7 fix: chmod inside the listen callback to eliminate the race
|
|
330
|
+
// window where the socket is world-readable before permissions are set.
|
|
331
|
+
const chmodSocket = () => {
|
|
332
|
+
if (platform() !== 'win32') {
|
|
333
|
+
try {
|
|
334
|
+
chmodSync(socketPath, 0o600);
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
process.stderr.write('[vurb] Warning: Could not restrict socket permissions.\n');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
};
|
|
329
341
|
await new Promise((resolve, reject) => {
|
|
330
342
|
server.once('error', (err) => {
|
|
331
343
|
if (err.code === 'EADDRINUSE') {
|
|
@@ -337,27 +349,16 @@ export async function createTelemetryBus(config) {
|
|
|
337
349
|
}
|
|
338
350
|
catch { /* ignore on Windows */ }
|
|
339
351
|
server.once('error', (retryErr) => reject(retryErr));
|
|
340
|
-
server.listen(socketPath, () => resolve());
|
|
352
|
+
server.listen(socketPath, () => { chmodSocket(); resolve(); });
|
|
341
353
|
}
|
|
342
354
|
else {
|
|
343
355
|
reject(err);
|
|
344
356
|
}
|
|
345
357
|
});
|
|
346
|
-
server.listen(socketPath, () => resolve());
|
|
358
|
+
server.listen(socketPath, () => { chmodSocket(); resolve(); });
|
|
347
359
|
});
|
|
348
360
|
// ── Registry: announce this server for auto-discovery ──
|
|
349
361
|
writeRegistryFile(process.pid, config?.path ? undefined : 'vurb');
|
|
350
|
-
// ── Gotcha #1: IPC Security (chmod 0o600) ─────────────
|
|
351
|
-
// Restrict socket to owner-only on POSIX to prevent PII sniffing
|
|
352
|
-
if (platform() !== 'win32') {
|
|
353
|
-
try {
|
|
354
|
-
chmodSync(socketPath, 0o600);
|
|
355
|
-
}
|
|
356
|
-
catch {
|
|
357
|
-
// Non-fatal — log warning via stderr (never stdout!)
|
|
358
|
-
process.stderr.write('[vurb] Warning: Could not restrict socket permissions.\n');
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
362
|
// ── Heartbeat Timer ───────────────────────────────────
|
|
362
363
|
const heartbeatTimer = setInterval(() => {
|
|
363
364
|
if (clients.size === 0)
|