@specverse/types 4.1.0 → 4.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/dist/spec-rules/curved-protocol.d.ts +52 -0
- package/dist/spec-rules/curved-protocol.d.ts.map +1 -0
- package/dist/spec-rules/curved-protocol.js +87 -0
- package/dist/spec-rules/curved-protocol.js.map +1 -0
- package/dist/spec-rules/field-classification.d.ts +65 -0
- package/dist/spec-rules/field-classification.d.ts.map +1 -0
- package/dist/spec-rules/field-classification.js +142 -0
- package/dist/spec-rules/field-classification.js.map +1 -0
- package/dist/spec-rules/index.d.ts +14 -0
- package/dist/spec-rules/index.d.ts.map +1 -0
- package/dist/spec-rules/index.js +14 -0
- package/dist/spec-rules/index.js.map +1 -0
- package/dist/spec-rules/lifecycle-rules.d.ts +57 -0
- package/dist/spec-rules/lifecycle-rules.d.ts.map +1 -0
- package/dist/spec-rules/lifecycle-rules.js +106 -0
- package/dist/spec-rules/lifecycle-rules.js.map +1 -0
- package/dist/spec-rules/route-derivation.d.ts +39 -0
- package/dist/spec-rules/route-derivation.d.ts.map +1 -0
- package/dist/spec-rules/route-derivation.js +68 -0
- package/dist/spec-rules/route-derivation.js.map +1 -0
- package/dist/spec-rules/spec-normalization.d.ts +43 -0
- package/dist/spec-rules/spec-normalization.d.ts.map +1 -0
- package/dist/spec-rules/spec-normalization.js +99 -0
- package/dist/spec-rules/spec-normalization.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CURVED Protocol Mapping — Shared Spec Behaviour Rules
|
|
3
|
+
*
|
|
4
|
+
* Maps CURVED operations (Create, Update, Retrieve, Validate, Evolve, Delete)
|
|
5
|
+
* to HTTP protocol details. Single source of truth used by:
|
|
6
|
+
* - generators (at build time to emit static route code)
|
|
7
|
+
* - @specverse/runtime (at render time for API calls)
|
|
8
|
+
* - app-demo (at request time for dynamic routing)
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* CURVED operations supported by SpecVerse
|
|
12
|
+
*/
|
|
13
|
+
export type CURVEDOperation = 'create' | 'update' | 'retrieve' | 'retrieve_many' | 'validate' | 'evolve' | 'delete';
|
|
14
|
+
/**
|
|
15
|
+
* Protocol mapping for a CURVED operation
|
|
16
|
+
*/
|
|
17
|
+
export interface ProtocolMapping {
|
|
18
|
+
method: string;
|
|
19
|
+
pathPattern: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Default CURVED → HTTP protocol mapping.
|
|
23
|
+
* Maps semantic CRUD operations to REST endpoints.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CURVED_PROTOCOL_MAPPING: Record<CURVEDOperation, ProtocolMapping>;
|
|
26
|
+
/**
|
|
27
|
+
* Infer HTTP method from operation name.
|
|
28
|
+
*/
|
|
29
|
+
export declare function inferHttpMethod(operation: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Infer canonical relative path from operation name.
|
|
32
|
+
* Returns path relative to the resource base (e.g., /, /:id, /:id/evolve).
|
|
33
|
+
*/
|
|
34
|
+
export declare function inferPath(operation: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Derive REST base path from model name.
|
|
37
|
+
* E.g., "Post" → "/api/posts", "Category" → "/api/categories"
|
|
38
|
+
*/
|
|
39
|
+
export declare function deriveBasePath(modelName: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Derived endpoint from a CURVED operation
|
|
42
|
+
*/
|
|
43
|
+
export interface DerivedEndpoint {
|
|
44
|
+
operation: string;
|
|
45
|
+
method: string;
|
|
46
|
+
path: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Convert CURED operations object to endpoint list.
|
|
50
|
+
*/
|
|
51
|
+
export declare function curedToEndpoints(cured: Record<string, any>): DerivedEndpoint[];
|
|
52
|
+
//# sourceMappingURL=curved-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curved-protocol.d.ts","sourceRoot":"","sources":["../../src/spec-rules/curved-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,eAAe,GACf,UAAU,GACV,QAAQ,GACR,QAAQ,CAAC;AAEb;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,eAAe,EAAE,eAAe,CAQ5E,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUzD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUnD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,eAAe,EAAE,CAY9E"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CURVED Protocol Mapping — Shared Spec Behaviour Rules
|
|
3
|
+
*
|
|
4
|
+
* Maps CURVED operations (Create, Update, Retrieve, Validate, Evolve, Delete)
|
|
5
|
+
* to HTTP protocol details. Single source of truth used by:
|
|
6
|
+
* - generators (at build time to emit static route code)
|
|
7
|
+
* - @specverse/runtime (at render time for API calls)
|
|
8
|
+
* - app-demo (at request time for dynamic routing)
|
|
9
|
+
*/
|
|
10
|
+
import { pluralize } from '../utils.js';
|
|
11
|
+
/**
|
|
12
|
+
* Default CURVED → HTTP protocol mapping.
|
|
13
|
+
* Maps semantic CRUD operations to REST endpoints.
|
|
14
|
+
*/
|
|
15
|
+
export const CURVED_PROTOCOL_MAPPING = {
|
|
16
|
+
create: { method: 'POST', pathPattern: '/api/{resource}' },
|
|
17
|
+
update: { method: 'PUT', pathPattern: '/api/{resource}/{id}' },
|
|
18
|
+
retrieve: { method: 'GET', pathPattern: '/api/{resource}/{id}' },
|
|
19
|
+
retrieve_many: { method: 'GET', pathPattern: '/api/{resource}' },
|
|
20
|
+
validate: { method: 'POST', pathPattern: '/api/{resource}/validate' },
|
|
21
|
+
evolve: { method: 'POST', pathPattern: '/api/{resource}/{id}/evolve' },
|
|
22
|
+
delete: { method: 'DELETE', pathPattern: '/api/{resource}/{id}' },
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Infer HTTP method from operation name.
|
|
26
|
+
*/
|
|
27
|
+
export function inferHttpMethod(operation) {
|
|
28
|
+
if (!operation)
|
|
29
|
+
return 'post';
|
|
30
|
+
const op = operation.toLowerCase();
|
|
31
|
+
if (op === 'create' || op === 'validate')
|
|
32
|
+
return 'post';
|
|
33
|
+
if (op === 'retrieve' || op === 'list' || op === 'retrieve_many')
|
|
34
|
+
return 'get';
|
|
35
|
+
if (op === 'update' || op === 'evolve')
|
|
36
|
+
return 'put';
|
|
37
|
+
if (op === 'delete')
|
|
38
|
+
return 'delete';
|
|
39
|
+
return 'post';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Infer canonical relative path from operation name.
|
|
43
|
+
* Returns path relative to the resource base (e.g., /, /:id, /:id/evolve).
|
|
44
|
+
*/
|
|
45
|
+
export function inferPath(operation) {
|
|
46
|
+
if (!operation)
|
|
47
|
+
return '/';
|
|
48
|
+
const op = operation.toLowerCase();
|
|
49
|
+
if (op === 'create' || op === 'list' || op === 'retrieve_many')
|
|
50
|
+
return '/';
|
|
51
|
+
if (op === 'retrieve' || op === 'update' || op === 'delete')
|
|
52
|
+
return '/:id';
|
|
53
|
+
if (op === 'evolve')
|
|
54
|
+
return '/:id/evolve';
|
|
55
|
+
if (op === 'validate')
|
|
56
|
+
return '/validate';
|
|
57
|
+
return `/${op}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Derive REST base path from model name.
|
|
61
|
+
* E.g., "Post" → "/api/posts", "Category" → "/api/categories"
|
|
62
|
+
*/
|
|
63
|
+
export function deriveBasePath(modelName) {
|
|
64
|
+
return `/api/${pluralize(modelName.toLowerCase())}`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Convert CURED operations object to endpoint list.
|
|
68
|
+
*/
|
|
69
|
+
export function curedToEndpoints(cured) {
|
|
70
|
+
const endpoints = [];
|
|
71
|
+
if (cured.create)
|
|
72
|
+
endpoints.push({ operation: 'create', method: 'POST', path: '/' });
|
|
73
|
+
if (cured.retrieve)
|
|
74
|
+
endpoints.push({ operation: 'retrieve', method: 'GET', path: '/:id' });
|
|
75
|
+
if (cured.retrieve_many)
|
|
76
|
+
endpoints.push({ operation: 'list', method: 'GET', path: '/' });
|
|
77
|
+
if (cured.update)
|
|
78
|
+
endpoints.push({ operation: 'update', method: 'PUT', path: '/:id' });
|
|
79
|
+
if (cured.evolve)
|
|
80
|
+
endpoints.push({ operation: 'evolve', method: 'PATCH', path: '/:id/evolve' });
|
|
81
|
+
if (cured.delete)
|
|
82
|
+
endpoints.push({ operation: 'delete', method: 'DELETE', path: '/:id' });
|
|
83
|
+
if (cured.validate)
|
|
84
|
+
endpoints.push({ operation: 'validate', method: 'POST', path: '/validate' });
|
|
85
|
+
return endpoints;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=curved-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curved-protocol.js","sourceRoot":"","sources":["../../src/spec-rules/curved-protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field Classification
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for classifying spec fields into categories:
|
|
5
|
+
* business, relationship, lifecycle, metadata, auto-generated.
|
|
6
|
+
*
|
|
7
|
+
* Framework-agnostic — no React or DOM dependencies.
|
|
8
|
+
*
|
|
9
|
+
* Consolidated from:
|
|
10
|
+
* - app-demo useEntityHelpers.ts (field separation, metadata detection)
|
|
11
|
+
* - engines field-helpers-generator.ts (auto-generated detection, defaults)
|
|
12
|
+
*/
|
|
13
|
+
/** Result of classifying a model's fields */
|
|
14
|
+
export interface FieldClassification {
|
|
15
|
+
/** Business logic fields (displayed prominently) */
|
|
16
|
+
regularFields: string[];
|
|
17
|
+
/** Metadata fields (id, timestamps — displayed at end, muted) */
|
|
18
|
+
metadataFields: string[];
|
|
19
|
+
}
|
|
20
|
+
/** Fields always treated as metadata (displayed last, muted styling) */
|
|
21
|
+
export declare const METADATA_FIELDS: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Classify fields into regular (business) vs metadata categories.
|
|
24
|
+
*
|
|
25
|
+
* Column order in views:
|
|
26
|
+
* 1. Regular business fields
|
|
27
|
+
* 2. Relationship fields (blue)
|
|
28
|
+
* 3. Lifecycle fields (purple badges)
|
|
29
|
+
* 4. Metadata fields (gray, italic)
|
|
30
|
+
*/
|
|
31
|
+
export declare function classifyFields(attributes: Record<string, unknown>): FieldClassification;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a field is auto-generated (should be hidden in forms).
|
|
34
|
+
*
|
|
35
|
+
* Detection:
|
|
36
|
+
* - Has `auto` property in schema (auto=now, auto=uuid4, etc.)
|
|
37
|
+
* - String format contains `auto=`
|
|
38
|
+
* - Common auto-generated DateTime fields (createdAt, updatedAt, etc.)
|
|
39
|
+
* - UUID id field
|
|
40
|
+
*/
|
|
41
|
+
export declare function isAutoField(attrName: string, attrDef: unknown): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Check if a field is a metadata field (id, timestamps).
|
|
44
|
+
*/
|
|
45
|
+
export declare function isMetadataField(name: string): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Check if a field represents a relationship (foreign key).
|
|
48
|
+
*/
|
|
49
|
+
export declare function isRelationshipField(name: string): boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Check if a field is required.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isFieldRequired(attrDef: unknown): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Get default value for a field based on its type.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getFieldDefaultValue(attrDef: unknown): unknown;
|
|
58
|
+
/**
|
|
59
|
+
* Initialize form data from schema attributes.
|
|
60
|
+
* Skips auto-generated fields by default.
|
|
61
|
+
*/
|
|
62
|
+
export declare function initializeFormData(attributes: Record<string, unknown>, options?: {
|
|
63
|
+
includeAutoGenerated?: boolean;
|
|
64
|
+
}): Record<string, unknown>;
|
|
65
|
+
//# sourceMappingURL=field-classification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-classification.d.ts","sourceRoot":"","sources":["../../src/spec-rules/field-classification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,oDAAoD;IACpD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,iEAAiE;IACjE,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,wEAAwE;AACxE,eAAO,MAAM,eAAe,UAE3B,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,mBAAmB,CAiBrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAqBvE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAMzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAgC9D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,OAAO,GAAE;IAAE,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWzB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field Classification
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for classifying spec fields into categories:
|
|
5
|
+
* business, relationship, lifecycle, metadata, auto-generated.
|
|
6
|
+
*
|
|
7
|
+
* Framework-agnostic — no React or DOM dependencies.
|
|
8
|
+
*
|
|
9
|
+
* Consolidated from:
|
|
10
|
+
* - app-demo useEntityHelpers.ts (field separation, metadata detection)
|
|
11
|
+
* - engines field-helpers-generator.ts (auto-generated detection, defaults)
|
|
12
|
+
*/
|
|
13
|
+
/** Fields always treated as metadata (displayed last, muted styling) */
|
|
14
|
+
export const METADATA_FIELDS = [
|
|
15
|
+
'id', 'createdAt', 'updatedAt', 'appliedAt', 'joinedAt', 'publishedAt',
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* Classify fields into regular (business) vs metadata categories.
|
|
19
|
+
*
|
|
20
|
+
* Column order in views:
|
|
21
|
+
* 1. Regular business fields
|
|
22
|
+
* 2. Relationship fields (blue)
|
|
23
|
+
* 3. Lifecycle fields (purple badges)
|
|
24
|
+
* 4. Metadata fields (gray, italic)
|
|
25
|
+
*/
|
|
26
|
+
export function classifyFields(attributes) {
|
|
27
|
+
if (!attributes || Object.keys(attributes).length === 0) {
|
|
28
|
+
return { regularFields: [], metadataFields: [] };
|
|
29
|
+
}
|
|
30
|
+
const regular = [];
|
|
31
|
+
const metadata = [];
|
|
32
|
+
for (const key of Object.keys(attributes)) {
|
|
33
|
+
if (METADATA_FIELDS.includes(key)) {
|
|
34
|
+
metadata.push(key);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
regular.push(key);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { regularFields: regular, metadataFields: metadata };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a field is auto-generated (should be hidden in forms).
|
|
44
|
+
*
|
|
45
|
+
* Detection:
|
|
46
|
+
* - Has `auto` property in schema (auto=now, auto=uuid4, etc.)
|
|
47
|
+
* - String format contains `auto=`
|
|
48
|
+
* - Common auto-generated DateTime fields (createdAt, updatedAt, etc.)
|
|
49
|
+
* - UUID id field
|
|
50
|
+
*/
|
|
51
|
+
export function isAutoField(attrName, attrDef) {
|
|
52
|
+
// Object format with auto property
|
|
53
|
+
if (attrDef && typeof attrDef === 'object' && attrDef.auto) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
const typeStr = typeof attrDef === 'string'
|
|
57
|
+
? attrDef
|
|
58
|
+
: attrDef?.type || '';
|
|
59
|
+
// String format with auto= modifier
|
|
60
|
+
if (typeStr.includes('auto='))
|
|
61
|
+
return true;
|
|
62
|
+
// UUID id field
|
|
63
|
+
if (attrName === 'id' && typeStr.includes('UUID'))
|
|
64
|
+
return true;
|
|
65
|
+
// Common auto-generated DateTime fields
|
|
66
|
+
const autoDateFields = ['createdAt', 'updatedAt', 'joinedAt', 'publishedAt'];
|
|
67
|
+
if (autoDateFields.includes(attrName) && typeStr.includes('DateTime'))
|
|
68
|
+
return true;
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if a field is a metadata field (id, timestamps).
|
|
73
|
+
*/
|
|
74
|
+
export function isMetadataField(name) {
|
|
75
|
+
return METADATA_FIELDS.includes(name);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check if a field represents a relationship (foreign key).
|
|
79
|
+
*/
|
|
80
|
+
export function isRelationshipField(name) {
|
|
81
|
+
return name.endsWith('Id') && name !== 'id';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if a field is required.
|
|
85
|
+
*/
|
|
86
|
+
export function isFieldRequired(attrDef) {
|
|
87
|
+
if (!attrDef)
|
|
88
|
+
return false;
|
|
89
|
+
if (typeof attrDef === 'string') {
|
|
90
|
+
return attrDef.toLowerCase().includes('required');
|
|
91
|
+
}
|
|
92
|
+
return attrDef.required === true;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get default value for a field based on its type.
|
|
96
|
+
*/
|
|
97
|
+
export function getFieldDefaultValue(attrDef) {
|
|
98
|
+
if (!attrDef)
|
|
99
|
+
return '';
|
|
100
|
+
const typeStr = typeof attrDef === 'string'
|
|
101
|
+
? attrDef
|
|
102
|
+
: attrDef?.type || '';
|
|
103
|
+
// Check if schema has an explicit default
|
|
104
|
+
if (attrDef && typeof attrDef === 'object' && attrDef.default !== undefined) {
|
|
105
|
+
const defaultVal = attrDef.default;
|
|
106
|
+
if (typeStr.toLowerCase().includes('bool')) {
|
|
107
|
+
return defaultVal === 'true' || defaultVal === true;
|
|
108
|
+
}
|
|
109
|
+
else if (typeStr.toLowerCase().includes('int') ||
|
|
110
|
+
typeStr.toLowerCase().includes('number')) {
|
|
111
|
+
return typeof defaultVal === 'number' ? defaultVal : parseInt(defaultVal) || 0;
|
|
112
|
+
}
|
|
113
|
+
else if (typeStr.toLowerCase().includes('string')) {
|
|
114
|
+
return typeof defaultVal === 'string'
|
|
115
|
+
? defaultVal.replace(/^"|"$/g, '')
|
|
116
|
+
: defaultVal;
|
|
117
|
+
}
|
|
118
|
+
return defaultVal;
|
|
119
|
+
}
|
|
120
|
+
// Type-based defaults
|
|
121
|
+
if (typeStr.toLowerCase().includes('bool'))
|
|
122
|
+
return false;
|
|
123
|
+
if (typeStr.toLowerCase().includes('int') ||
|
|
124
|
+
typeStr.toLowerCase().includes('number'))
|
|
125
|
+
return 0;
|
|
126
|
+
return '';
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Initialize form data from schema attributes.
|
|
130
|
+
* Skips auto-generated fields by default.
|
|
131
|
+
*/
|
|
132
|
+
export function initializeFormData(attributes, options = {}) {
|
|
133
|
+
const data = {};
|
|
134
|
+
for (const [attrName, attrDef] of Object.entries(attributes)) {
|
|
135
|
+
if (!options.includeAutoGenerated && isAutoField(attrName, attrDef)) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
data[attrName] = getFieldDefaultValue(attrDef);
|
|
139
|
+
}
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=field-classification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-classification.js","sourceRoot":"","sources":["../../src/spec-rules/field-classification.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Rules — Shared Behaviour Layer
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that derive behaviour from SpecVerse specifications.
|
|
5
|
+
* Used by generators (at build time) and runtime engines (at request time).
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { type CURVEDOperation, type ProtocolMapping, type DerivedEndpoint, CURVED_PROTOCOL_MAPPING, inferHttpMethod, inferPath, deriveBasePath, curedToEndpoints, } from './curved-protocol.js';
|
|
10
|
+
export { type LifecycleFlowResult, parseLifecycleFlow, normalizeSpec, } from './spec-normalization.js';
|
|
11
|
+
export { type LifecycleDefinition, type TransitionMap, buildTransitionMap, isValidTransition, getAvailableTransitions, resolveInitialState, } from './lifecycle-rules.js';
|
|
12
|
+
export { type FieldClassification, METADATA_FIELDS, classifyFields, isAutoField, isMetadataField, isRelationshipField, isFieldRequired, getFieldDefaultValue, initializeFormData, } from './field-classification.js';
|
|
13
|
+
export { type DerivedRoute, deriveRoutes, } from './route-derivation.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/spec-rules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,uBAAuB,EACvB,eAAe,EACf,SAAS,EACT,cAAc,EACd,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,KAAK,mBAAmB,EACxB,kBAAkB,EAClB,aAAa,GACd,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,KAAK,mBAAmB,EACxB,eAAe,EACf,cAAc,EACd,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,KAAK,YAAY,EACjB,YAAY,GACb,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Rules — Shared Behaviour Layer
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that derive behaviour from SpecVerse specifications.
|
|
5
|
+
* Used by generators (at build time) and runtime engines (at request time).
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { CURVED_PROTOCOL_MAPPING, inferHttpMethod, inferPath, deriveBasePath, curedToEndpoints, } from './curved-protocol.js';
|
|
10
|
+
export { parseLifecycleFlow, normalizeSpec, } from './spec-normalization.js';
|
|
11
|
+
export { buildTransitionMap, isValidTransition, getAvailableTransitions, resolveInitialState, } from './lifecycle-rules.js';
|
|
12
|
+
export { METADATA_FIELDS, classifyFields, isAutoField, isMetadataField, isRelationshipField, isFieldRequired, getFieldDefaultValue, initializeFormData, } from './field-classification.js';
|
|
13
|
+
export { deriveRoutes, } from './route-derivation.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/spec-rules/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAIL,uBAAuB,EACvB,eAAe,EACf,SAAS,EACT,cAAc,EACd,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,kBAAkB,EAClB,aAAa,GACd,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAGL,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,eAAe,EACf,cAAc,EACd,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAEL,YAAY,GACb,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Rules — Shared Spec Behaviour Rules
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for lifecycle state machine logic:
|
|
5
|
+
* - Building transition maps from lifecycle definitions
|
|
6
|
+
* - Validating transitions
|
|
7
|
+
* - Resolving initial states
|
|
8
|
+
*
|
|
9
|
+
* Single source of truth used by:
|
|
10
|
+
* - generators (at build time to emit static transition maps)
|
|
11
|
+
* - @specverse/runtime (for lifecycle UI controls)
|
|
12
|
+
* - app-demo (at runtime for transition validation)
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Lifecycle definition — accepts all formats from spec
|
|
16
|
+
*/
|
|
17
|
+
export interface LifecycleDefinition {
|
|
18
|
+
name?: string;
|
|
19
|
+
states?: Array<string | {
|
|
20
|
+
name: string;
|
|
21
|
+
}>;
|
|
22
|
+
transitions?: Array<{
|
|
23
|
+
from: string | string[];
|
|
24
|
+
to: string;
|
|
25
|
+
}> | Record<string, any>;
|
|
26
|
+
flow?: string;
|
|
27
|
+
initialState?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Map of valid transitions: fromState → [toState1, toState2, ...]
|
|
31
|
+
*/
|
|
32
|
+
export interface TransitionMap {
|
|
33
|
+
[fromState: string]: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Build a transition map from a lifecycle definition.
|
|
37
|
+
*
|
|
38
|
+
* Handles all input formats:
|
|
39
|
+
* - flow string: "draft -> open -> closed"
|
|
40
|
+
* - states array with default linear transitions
|
|
41
|
+
* - explicit transitions array: [{from, to}]
|
|
42
|
+
* - mixed: states + explicit transitions (explicit override linear defaults)
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildTransitionMap(lifecycle: LifecycleDefinition): TransitionMap;
|
|
45
|
+
/**
|
|
46
|
+
* Check if a specific transition is valid.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isValidTransition(map: TransitionMap, from: string, to: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Get all valid next states from current state.
|
|
51
|
+
*/
|
|
52
|
+
export declare function getAvailableTransitions(map: TransitionMap, currentState: string): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the initial state from a lifecycle definition.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveInitialState(lifecycle: LifecycleDefinition): string;
|
|
57
|
+
//# sourceMappingURL=lifecycle-rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-rules.d.ts","sourceRoot":"","sources":["../../src/spec-rules/lifecycle-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1C,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC/B;AASD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,mBAAmB,GAAG,aAAa,CA6ChF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEvF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAE1F;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,mBAAmB,GAAG,MAAM,CAgB1E"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Rules — Shared Spec Behaviour Rules
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for lifecycle state machine logic:
|
|
5
|
+
* - Building transition maps from lifecycle definitions
|
|
6
|
+
* - Validating transitions
|
|
7
|
+
* - Resolving initial states
|
|
8
|
+
*
|
|
9
|
+
* Single source of truth used by:
|
|
10
|
+
* - generators (at build time to emit static transition maps)
|
|
11
|
+
* - @specverse/runtime (for lifecycle UI controls)
|
|
12
|
+
* - app-demo (at runtime for transition validation)
|
|
13
|
+
*/
|
|
14
|
+
import { parseLifecycleFlow } from './spec-normalization.js';
|
|
15
|
+
/**
|
|
16
|
+
* Extract state name from various formats
|
|
17
|
+
*/
|
|
18
|
+
function stateName(s) {
|
|
19
|
+
return typeof s === 'string' ? s : s.name;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build a transition map from a lifecycle definition.
|
|
23
|
+
*
|
|
24
|
+
* Handles all input formats:
|
|
25
|
+
* - flow string: "draft -> open -> closed"
|
|
26
|
+
* - states array with default linear transitions
|
|
27
|
+
* - explicit transitions array: [{from, to}]
|
|
28
|
+
* - mixed: states + explicit transitions (explicit override linear defaults)
|
|
29
|
+
*/
|
|
30
|
+
export function buildTransitionMap(lifecycle) {
|
|
31
|
+
const map = {};
|
|
32
|
+
// If there's a flow string and no states, parse it first
|
|
33
|
+
let states = lifecycle.states || [];
|
|
34
|
+
if (lifecycle.flow && typeof lifecycle.flow === 'string' && states.length === 0) {
|
|
35
|
+
const parsed = parseLifecycleFlow(lifecycle.flow);
|
|
36
|
+
states = parsed.states;
|
|
37
|
+
// If no explicit transitions, use the flow-derived ones
|
|
38
|
+
if (!lifecycle.transitions) {
|
|
39
|
+
for (const t of parsed.transitions) {
|
|
40
|
+
if (!map[t.from])
|
|
41
|
+
map[t.from] = [];
|
|
42
|
+
if (!map[t.from].includes(t.to))
|
|
43
|
+
map[t.from].push(t.to);
|
|
44
|
+
}
|
|
45
|
+
return map;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Default linear transitions from states array: state[i] → state[i+1]
|
|
49
|
+
const stateNames = states.map(stateName);
|
|
50
|
+
if (stateNames.length > 1) {
|
|
51
|
+
for (let i = 0; i < stateNames.length - 1; i++) {
|
|
52
|
+
if (!map[stateNames[i]])
|
|
53
|
+
map[stateNames[i]] = [];
|
|
54
|
+
if (!map[stateNames[i]].includes(stateNames[i + 1])) {
|
|
55
|
+
map[stateNames[i]].push(stateNames[i + 1]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Override/extend with explicit transitions
|
|
60
|
+
if (lifecycle.transitions) {
|
|
61
|
+
const transitions = Array.isArray(lifecycle.transitions)
|
|
62
|
+
? lifecycle.transitions
|
|
63
|
+
: Object.entries(lifecycle.transitions).map(([name, t]) => ({ name, ...t }));
|
|
64
|
+
for (const t of transitions) {
|
|
65
|
+
const fromStates = Array.isArray(t.from) ? t.from : [t.from];
|
|
66
|
+
for (const from of fromStates) {
|
|
67
|
+
if (!map[from])
|
|
68
|
+
map[from] = [];
|
|
69
|
+
if (!map[from].includes(t.to))
|
|
70
|
+
map[from].push(t.to);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return map;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if a specific transition is valid.
|
|
78
|
+
*/
|
|
79
|
+
export function isValidTransition(map, from, to) {
|
|
80
|
+
return (map[from] || []).includes(to);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get all valid next states from current state.
|
|
84
|
+
*/
|
|
85
|
+
export function getAvailableTransitions(map, currentState) {
|
|
86
|
+
return map[currentState] || [];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the initial state from a lifecycle definition.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveInitialState(lifecycle) {
|
|
92
|
+
// Explicit initialState
|
|
93
|
+
if (lifecycle.initialState)
|
|
94
|
+
return lifecycle.initialState;
|
|
95
|
+
// First state in states array
|
|
96
|
+
if (lifecycle.states && lifecycle.states.length > 0) {
|
|
97
|
+
return stateName(lifecycle.states[0]);
|
|
98
|
+
}
|
|
99
|
+
// Parse from flow string
|
|
100
|
+
if (lifecycle.flow && typeof lifecycle.flow === 'string') {
|
|
101
|
+
const parsed = parseLifecycleFlow(lifecycle.flow);
|
|
102
|
+
return parsed.initialState;
|
|
103
|
+
}
|
|
104
|
+
return '';
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=lifecycle-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-rules.js","sourceRoot":"","sources":["../../src/spec-rules/lifecycle-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route Derivation — Shared Spec Behaviour Rules
|
|
3
|
+
*
|
|
4
|
+
* Derives a route table from a controller's CURVED operations.
|
|
5
|
+
* Single source of truth used by:
|
|
6
|
+
* - generators (to emit static route registrations)
|
|
7
|
+
* - app-demo (to register dynamic routes)
|
|
8
|
+
*/
|
|
9
|
+
import { deriveBasePath } from './curved-protocol.js';
|
|
10
|
+
/**
|
|
11
|
+
* A derived route from a controller's operations
|
|
12
|
+
*/
|
|
13
|
+
export interface DerivedRoute {
|
|
14
|
+
/** Operation name (create, retrieve, list, update, evolve, delete, validate) */
|
|
15
|
+
operation: string;
|
|
16
|
+
/** HTTP method (GET, POST, PUT, DELETE, PATCH) */
|
|
17
|
+
method: string;
|
|
18
|
+
/** Relative path (/, /:id, /:id/evolve, /validate) */
|
|
19
|
+
relativePath: string;
|
|
20
|
+
/** Whether the route takes an :id parameter */
|
|
21
|
+
hasIdParam: boolean;
|
|
22
|
+
/** Whether the route expects a request body */
|
|
23
|
+
hasBody: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Derive a complete route table from a controller definition.
|
|
27
|
+
*
|
|
28
|
+
* Accepts controllers with CURED operations and/or explicit endpoints.
|
|
29
|
+
*/
|
|
30
|
+
export declare function deriveRoutes(controller: {
|
|
31
|
+
cured?: Record<string, any>;
|
|
32
|
+
endpoints?: Array<{
|
|
33
|
+
operation: string;
|
|
34
|
+
method?: string;
|
|
35
|
+
path?: string;
|
|
36
|
+
}>;
|
|
37
|
+
}): DerivedRoute[];
|
|
38
|
+
export { deriveBasePath };
|
|
39
|
+
//# sourceMappingURL=route-derivation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-derivation.d.ts","sourceRoot":"","sources":["../../src/spec-rules/route-derivation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAA8B,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAElF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1E,GAAG,YAAY,EAAE,CA+CjB;AAGD,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route Derivation — Shared Spec Behaviour Rules
|
|
3
|
+
*
|
|
4
|
+
* Derives a route table from a controller's CURVED operations.
|
|
5
|
+
* Single source of truth used by:
|
|
6
|
+
* - generators (to emit static route registrations)
|
|
7
|
+
* - app-demo (to register dynamic routes)
|
|
8
|
+
*/
|
|
9
|
+
import { inferHttpMethod, inferPath, deriveBasePath } from './curved-protocol.js';
|
|
10
|
+
/**
|
|
11
|
+
* Derive a complete route table from a controller definition.
|
|
12
|
+
*
|
|
13
|
+
* Accepts controllers with CURED operations and/or explicit endpoints.
|
|
14
|
+
*/
|
|
15
|
+
export function deriveRoutes(controller) {
|
|
16
|
+
const routes = [];
|
|
17
|
+
// From CURED operations
|
|
18
|
+
if (controller.cured) {
|
|
19
|
+
const ops = controller.cured;
|
|
20
|
+
const addRoute = (operation) => {
|
|
21
|
+
const method = inferHttpMethod(operation).toUpperCase();
|
|
22
|
+
const path = inferPath(operation);
|
|
23
|
+
routes.push({
|
|
24
|
+
operation,
|
|
25
|
+
method,
|
|
26
|
+
relativePath: path,
|
|
27
|
+
hasIdParam: path.includes(':id'),
|
|
28
|
+
hasBody: ['POST', 'PUT', 'PATCH'].includes(method),
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
if (ops.create)
|
|
32
|
+
addRoute('create');
|
|
33
|
+
if (ops.retrieve)
|
|
34
|
+
addRoute('retrieve');
|
|
35
|
+
if (ops.retrieve_many) {
|
|
36
|
+
addRoute('list');
|
|
37
|
+
}
|
|
38
|
+
if (ops.update)
|
|
39
|
+
addRoute('update');
|
|
40
|
+
if (ops.evolve)
|
|
41
|
+
addRoute('evolve');
|
|
42
|
+
if (ops.delete)
|
|
43
|
+
addRoute('delete');
|
|
44
|
+
if (ops.validate)
|
|
45
|
+
addRoute('validate');
|
|
46
|
+
}
|
|
47
|
+
// From explicit endpoints (override or supplement CURED)
|
|
48
|
+
if (controller.endpoints) {
|
|
49
|
+
for (const ep of controller.endpoints) {
|
|
50
|
+
const method = (ep.method || inferHttpMethod(ep.operation)).toUpperCase();
|
|
51
|
+
const path = ep.path || inferPath(ep.operation);
|
|
52
|
+
// Don't duplicate if same operation already from CURED
|
|
53
|
+
if (!routes.find(r => r.operation === ep.operation)) {
|
|
54
|
+
routes.push({
|
|
55
|
+
operation: ep.operation,
|
|
56
|
+
method,
|
|
57
|
+
relativePath: path,
|
|
58
|
+
hasIdParam: path.includes(':id'),
|
|
59
|
+
hasBody: ['POST', 'PUT', 'PATCH'].includes(method),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return routes;
|
|
65
|
+
}
|
|
66
|
+
// Re-export deriveBasePath for convenience (lives in curved-protocol)
|
|
67
|
+
export { deriveBasePath };
|
|
68
|
+
//# sourceMappingURL=route-derivation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-derivation.js","sourceRoot":"","sources":["../../src/spec-rules/route-derivation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAkBlF;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,UAG5B;IACC,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,wBAAwB;IACxB,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC;QAE7B,MAAM,QAAQ,GAAG,CAAC,SAAiB,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;YACxD,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC;gBACV,SAAS;gBACT,MAAM;gBACN,YAAY,EAAE,IAAI;gBAClB,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAChC,OAAO,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;aACnD,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,IAAI,GAAG,CAAC,MAAM;YAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,GAAG,CAAC,QAAQ;YAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;YAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC;QAC5C,IAAI,GAAG,CAAC,MAAM;YAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,GAAG,CAAC,MAAM;YAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,GAAG,CAAC,MAAM;YAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,GAAG,CAAC,QAAQ;YAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;IACzC,CAAC;IAED,yDAAyD;IACzD,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;QACzB,KAAK,MAAM,EAAE,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,IAAI,eAAe,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAC1E,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,IAAI,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;YAChD,uDAAuD;YACvD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;gBACpD,MAAM,CAAC,IAAI,CAAC;oBACV,SAAS,EAAE,EAAE,CAAC,SAAS;oBACvB,MAAM;oBACN,YAAY,EAAE,IAAI;oBAClB,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;oBAChC,OAAO,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;iBACnD,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sEAAsE;AACtE,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Normalization — Shared Spec Behaviour Rules
|
|
3
|
+
*
|
|
4
|
+
* Converts spec data between formats:
|
|
5
|
+
* - Array-format attributes/relationships/lifecycles → object format
|
|
6
|
+
* - Lifecycle flow strings → states + transitions
|
|
7
|
+
* - Array model references → single model string
|
|
8
|
+
*
|
|
9
|
+
* Single source of truth used by:
|
|
10
|
+
* - generators (server-generator embeds normalized spec)
|
|
11
|
+
* - @specverse/runtime (frontend consumes normalized schemas)
|
|
12
|
+
* - app-demo (dynamic engine normalizes on load)
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Parsed lifecycle flow result
|
|
16
|
+
*/
|
|
17
|
+
export interface LifecycleFlowResult {
|
|
18
|
+
states: Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
}>;
|
|
21
|
+
transitions: Array<{
|
|
22
|
+
from: string;
|
|
23
|
+
to: string;
|
|
24
|
+
}>;
|
|
25
|
+
initialState: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse a lifecycle flow string into states and transitions.
|
|
29
|
+
* E.g., "draft -> open -> closed" → { states: [{name:"draft"}, ...], transitions: [{from:"draft",to:"open"}, ...] }
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseLifecycleFlow(flow: string): LifecycleFlowResult;
|
|
32
|
+
/**
|
|
33
|
+
* Normalize spec data for consistent consumption.
|
|
34
|
+
*
|
|
35
|
+
* Converts:
|
|
36
|
+
* - Array attributes → object keyed by name
|
|
37
|
+
* - Array relationships → object keyed by name (with targetModel alias)
|
|
38
|
+
* - Array lifecycles → object keyed by name
|
|
39
|
+
* - Lifecycle flow strings → parsed states + transitions
|
|
40
|
+
* - Array view.model → single string (first element)
|
|
41
|
+
*/
|
|
42
|
+
export declare function normalizeSpec(spec: any): any;
|
|
43
|
+
//# sourceMappingURL=spec-normalization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-normalization.d.ts","sourceRoot":"","sources":["../../src/spec-rules/spec-normalization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChC,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAcpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAwD5C"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Normalization — Shared Spec Behaviour Rules
|
|
3
|
+
*
|
|
4
|
+
* Converts spec data between formats:
|
|
5
|
+
* - Array-format attributes/relationships/lifecycles → object format
|
|
6
|
+
* - Lifecycle flow strings → states + transitions
|
|
7
|
+
* - Array model references → single model string
|
|
8
|
+
*
|
|
9
|
+
* Single source of truth used by:
|
|
10
|
+
* - generators (server-generator embeds normalized spec)
|
|
11
|
+
* - @specverse/runtime (frontend consumes normalized schemas)
|
|
12
|
+
* - app-demo (dynamic engine normalizes on load)
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Parse a lifecycle flow string into states and transitions.
|
|
16
|
+
* E.g., "draft -> open -> closed" → { states: [{name:"draft"}, ...], transitions: [{from:"draft",to:"open"}, ...] }
|
|
17
|
+
*/
|
|
18
|
+
export function parseLifecycleFlow(flow) {
|
|
19
|
+
const stateNames = flow.split('->').map(s => s.trim()).filter(Boolean);
|
|
20
|
+
const states = stateNames.map(s => ({ name: s }));
|
|
21
|
+
const transitions = [];
|
|
22
|
+
for (let i = 0; i < stateNames.length - 1; i++) {
|
|
23
|
+
transitions.push({ from: stateNames[i], to: stateNames[i + 1] });
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
states,
|
|
27
|
+
transitions,
|
|
28
|
+
initialState: stateNames[0] || '',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Normalize spec data for consistent consumption.
|
|
33
|
+
*
|
|
34
|
+
* Converts:
|
|
35
|
+
* - Array attributes → object keyed by name
|
|
36
|
+
* - Array relationships → object keyed by name (with targetModel alias)
|
|
37
|
+
* - Array lifecycles → object keyed by name
|
|
38
|
+
* - Lifecycle flow strings → parsed states + transitions
|
|
39
|
+
* - Array view.model → single string (first element)
|
|
40
|
+
*/
|
|
41
|
+
export function normalizeSpec(spec) {
|
|
42
|
+
if (!spec)
|
|
43
|
+
return spec;
|
|
44
|
+
const normalized = { ...spec };
|
|
45
|
+
// Normalize models
|
|
46
|
+
if (normalized.models && typeof normalized.models === 'object') {
|
|
47
|
+
for (const [_name, model] of Object.entries(normalized.models)) {
|
|
48
|
+
// Attributes: [{name:'id', type:'UUID'}] → {id: {type:'UUID'}}
|
|
49
|
+
if (Array.isArray(model.attributes)) {
|
|
50
|
+
const obj = {};
|
|
51
|
+
for (const attr of model.attributes) {
|
|
52
|
+
if (attr.name)
|
|
53
|
+
obj[attr.name] = attr;
|
|
54
|
+
}
|
|
55
|
+
model.attributes = obj;
|
|
56
|
+
}
|
|
57
|
+
// Relationships: [{name:'author', type:'belongsTo', target:'Author'}] → {author: {..., targetModel:'Author'}}
|
|
58
|
+
if (Array.isArray(model.relationships)) {
|
|
59
|
+
const obj = {};
|
|
60
|
+
for (const rel of model.relationships) {
|
|
61
|
+
if (rel.name)
|
|
62
|
+
obj[rel.name] = { ...rel, targetModel: rel.target || rel.targetModel };
|
|
63
|
+
}
|
|
64
|
+
model.relationships = obj;
|
|
65
|
+
}
|
|
66
|
+
// Lifecycles: [{name:'status', states:[...]}] → {status: {states:[...]}}
|
|
67
|
+
if (Array.isArray(model.lifecycles)) {
|
|
68
|
+
const obj = {};
|
|
69
|
+
for (const lc of model.lifecycles) {
|
|
70
|
+
if (lc.name)
|
|
71
|
+
obj[lc.name] = lc;
|
|
72
|
+
}
|
|
73
|
+
model.lifecycles = obj;
|
|
74
|
+
}
|
|
75
|
+
// Parse flow strings in lifecycles
|
|
76
|
+
if (model.lifecycles && typeof model.lifecycles === 'object') {
|
|
77
|
+
for (const lc of Object.values(model.lifecycles)) {
|
|
78
|
+
if (lc.flow && typeof lc.flow === 'string' && !lc.states) {
|
|
79
|
+
const result = parseLifecycleFlow(lc.flow);
|
|
80
|
+
lc.states = result.states;
|
|
81
|
+
lc.transitions = result.transitions;
|
|
82
|
+
lc.initialState = result.initialState;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Normalize views — ensure model is a single string
|
|
89
|
+
if (normalized.views && typeof normalized.views === 'object') {
|
|
90
|
+
for (const view of Object.values(normalized.views)) {
|
|
91
|
+
if (Array.isArray(view.model)) {
|
|
92
|
+
view.models = view.model;
|
|
93
|
+
view.model = view.model[0];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return normalized;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=spec-normalization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-normalization.js","sourceRoot":"","sources":["../../src/spec-rules/spec-normalization.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specverse/types",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.1",
|
|
4
4
|
"description": "Shared type definitions for SpecVerse engines \u2014 AST, specs, engine interfaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
"import": "./dist/index.js",
|
|
12
12
|
"default": "./dist/index.js"
|
|
13
13
|
},
|
|
14
|
+
"./spec-rules": {
|
|
15
|
+
"types": "./dist/spec-rules/index.d.ts",
|
|
16
|
+
"import": "./dist/spec-rules/index.js",
|
|
17
|
+
"default": "./dist/spec-rules/index.js"
|
|
18
|
+
},
|
|
14
19
|
"./package.json": "./package.json"
|
|
15
20
|
},
|
|
16
21
|
"scripts": {
|