@danypops/vehicle-core 0.1.0 → 0.1.1
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 +1 -1
- package/dist/vehicle-contract.d.ts +15 -0
- package/dist/vehicle-contract.js +40 -0
- package/package.json +2 -2
- package/src/vehicle-contract.ts +45 -0
package/README.md
CHANGED
|
@@ -10,6 +10,6 @@ bun add @danypops/vehicle-core
|
|
|
10
10
|
|
|
11
11
|
`defineVehicleOperation()`/`bindVehicleOperation()` build a serializable
|
|
12
12
|
descriptor kept separate from its executable handler. See the
|
|
13
|
-
[workspace README](https://github.com/DanyPops/
|
|
13
|
+
[workspace README](https://github.com/DanyPops/vehicle#readme) for how it
|
|
14
14
|
fits with `@danypops/vehicle-server`, `@danypops/vehicle-client`, and
|
|
15
15
|
`@danypops/vehicle-client-pi`.
|
|
@@ -19,6 +19,21 @@ export interface VehicleSchemaCodec<T> {
|
|
|
19
19
|
safeParse(value: unknown): VehicleSchemaResult<T>;
|
|
20
20
|
}
|
|
21
21
|
export declare function defineVehicleSchema<T>(codec: VehicleSchemaCodec<T>): VehicleSchemaCodec<T>;
|
|
22
|
+
export interface LooseObjectProperty {
|
|
23
|
+
readonly type: string;
|
|
24
|
+
readonly enum?: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A VehicleRegistry only ever calls a schema's own safeParse -- jsonSchema is
|
|
28
|
+
* descriptive metadata surfaced to a client/Pi projection, never itself
|
|
29
|
+
* enforced at runtime -- so a declared `enum` has to be checked here for
|
|
30
|
+
* real, or it's a documentation gesture, not an honest contract. Every
|
|
31
|
+
* consumer projecting a plain-object input onto a VehicleOperation needs the
|
|
32
|
+
* same required/enum checks; this is that check written once.
|
|
33
|
+
*/
|
|
34
|
+
export declare function defineLooseObjectSchema(properties: Record<string, LooseObjectProperty>, required?: readonly string[]): VehicleSchemaCodec<Record<string, unknown>>;
|
|
35
|
+
/** Accepts any value unvalidated -- for an operation whose output shape isn't worth a dedicated schema (an internal/low-stakes result, or one already validated upstream by the domain logic it wraps). */
|
|
36
|
+
export declare const passthroughVehicleSchema: VehicleSchemaCodec<unknown>;
|
|
22
37
|
export type VehicleEffect = "read" | "local-write" | "external-write" | "destructive" | "open-world";
|
|
23
38
|
export type VehicleIdempotency = {
|
|
24
39
|
readonly mode: "safe";
|
package/dist/vehicle-contract.js
CHANGED
|
@@ -4,6 +4,46 @@ export function defineVehicleSchema(codec) {
|
|
|
4
4
|
safeParse: codec.safeParse,
|
|
5
5
|
});
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* A VehicleRegistry only ever calls a schema's own safeParse -- jsonSchema is
|
|
9
|
+
* descriptive metadata surfaced to a client/Pi projection, never itself
|
|
10
|
+
* enforced at runtime -- so a declared `enum` has to be checked here for
|
|
11
|
+
* real, or it's a documentation gesture, not an honest contract. Every
|
|
12
|
+
* consumer projecting a plain-object input onto a VehicleOperation needs the
|
|
13
|
+
* same required/enum checks; this is that check written once.
|
|
14
|
+
*/
|
|
15
|
+
export function defineLooseObjectSchema(properties, required = []) {
|
|
16
|
+
return defineVehicleSchema({
|
|
17
|
+
// LooseObjectProperty's named fields (type, enum) are all JSON-value-shaped
|
|
18
|
+
// at runtime, but TypeScript's structural check against the recursive
|
|
19
|
+
// JsonValue union doesn't see that through a plain interface -- the cast
|
|
20
|
+
// is a type-system limitation, not a runtime concern.
|
|
21
|
+
jsonSchema: { type: "object", properties: properties, required: [...required], additionalProperties: false },
|
|
22
|
+
safeParse(value) {
|
|
23
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
24
|
+
return { success: false, issues: [{ path: [], message: "input must be an object" }] };
|
|
25
|
+
}
|
|
26
|
+
const input = value;
|
|
27
|
+
for (const key of required) {
|
|
28
|
+
if (!(key in input))
|
|
29
|
+
return { success: false, issues: [{ path: [key], message: `${key} is required` }] };
|
|
30
|
+
}
|
|
31
|
+
for (const [key, schema] of Object.entries(properties)) {
|
|
32
|
+
if (!schema.enum || !(key in input))
|
|
33
|
+
continue;
|
|
34
|
+
if (!schema.enum.includes(input[key])) {
|
|
35
|
+
return { success: false, issues: [{ path: [key], message: `${key} must be one of ${schema.enum.join(", ")}` }] };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { success: true, value: input };
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Accepts any value unvalidated -- for an operation whose output shape isn't worth a dedicated schema (an internal/low-stakes result, or one already validated upstream by the domain logic it wraps). */
|
|
43
|
+
export const passthroughVehicleSchema = defineVehicleSchema({
|
|
44
|
+
jsonSchema: { type: "object" },
|
|
45
|
+
safeParse: (value) => ({ success: true, value }),
|
|
46
|
+
});
|
|
7
47
|
export function defineVehicleOperation(options) {
|
|
8
48
|
validateOperationMetadata(options);
|
|
9
49
|
const descriptor = Object.freeze({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/vehicle-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Vehicle's runtime-neutral wire contract: operation descriptors, schema codecs, failure shapes. Zero runtime dependencies, zero Bun-specific code -- the one thing every Vehicle client and server package depends on.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/DanyPops/
|
|
26
|
+
"url": "git+https://github.com/DanyPops/vehicle.git",
|
|
27
27
|
"directory": "packages/vehicle-core"
|
|
28
28
|
},
|
|
29
29
|
"keywords": ["vehicle", "agent-tools"],
|
package/src/vehicle-contract.ts
CHANGED
|
@@ -23,6 +23,51 @@ export function defineVehicleSchema<T>(codec: VehicleSchemaCodec<T>): VehicleSch
|
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export interface LooseObjectProperty {
|
|
27
|
+
readonly type: string;
|
|
28
|
+
readonly enum?: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A VehicleRegistry only ever calls a schema's own safeParse -- jsonSchema is
|
|
33
|
+
* descriptive metadata surfaced to a client/Pi projection, never itself
|
|
34
|
+
* enforced at runtime -- so a declared `enum` has to be checked here for
|
|
35
|
+
* real, or it's a documentation gesture, not an honest contract. Every
|
|
36
|
+
* consumer projecting a plain-object input onto a VehicleOperation needs the
|
|
37
|
+
* same required/enum checks; this is that check written once.
|
|
38
|
+
*/
|
|
39
|
+
export function defineLooseObjectSchema(properties: Record<string, LooseObjectProperty>, required: readonly string[] = []): VehicleSchemaCodec<Record<string, unknown>> {
|
|
40
|
+
return defineVehicleSchema<Record<string, unknown>>({
|
|
41
|
+
// LooseObjectProperty's named fields (type, enum) are all JSON-value-shaped
|
|
42
|
+
// at runtime, but TypeScript's structural check against the recursive
|
|
43
|
+
// JsonValue union doesn't see that through a plain interface -- the cast
|
|
44
|
+
// is a type-system limitation, not a runtime concern.
|
|
45
|
+
jsonSchema: { type: "object", properties: properties as unknown as JsonValue, required: [...required], additionalProperties: false },
|
|
46
|
+
safeParse(value) {
|
|
47
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
48
|
+
return { success: false, issues: [{ path: [], message: "input must be an object" }] };
|
|
49
|
+
}
|
|
50
|
+
const input = value as Record<string, unknown>;
|
|
51
|
+
for (const key of required) {
|
|
52
|
+
if (!(key in input)) return { success: false, issues: [{ path: [key], message: `${key} is required` }] };
|
|
53
|
+
}
|
|
54
|
+
for (const [key, schema] of Object.entries(properties)) {
|
|
55
|
+
if (!schema.enum || !(key in input)) continue;
|
|
56
|
+
if (!schema.enum.includes(input[key] as string)) {
|
|
57
|
+
return { success: false, issues: [{ path: [key], message: `${key} must be one of ${schema.enum.join(", ")}` }] };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { success: true, value: input };
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Accepts any value unvalidated -- for an operation whose output shape isn't worth a dedicated schema (an internal/low-stakes result, or one already validated upstream by the domain logic it wraps). */
|
|
66
|
+
export const passthroughVehicleSchema: VehicleSchemaCodec<unknown> = defineVehicleSchema<unknown>({
|
|
67
|
+
jsonSchema: { type: "object" },
|
|
68
|
+
safeParse: (value) => ({ success: true, value }),
|
|
69
|
+
});
|
|
70
|
+
|
|
26
71
|
export type VehicleEffect = "read" | "local-write" | "external-write" | "destructive" | "open-world";
|
|
27
72
|
|
|
28
73
|
export type VehicleIdempotency =
|