@stonecrop/utilities 0.13.12 → 0.13.14
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/package.json +1 -1
- package/dist/assets/sample_data/coa.json +0 -2362
- package/dist/assets/sample_data/financials.json +0 -252
- package/dist/assets/sample_data/forecast.json +0 -2792
- package/dist/assets/sample_data/forecast_items.json +0 -126
- package/dist/assets/sample_data/http_logs.json +0 -252
- package/dist/assets/sample_data/inbox.json +0 -355
- package/dist/assets/sample_data/parts.json +0 -1322
- package/dist/assets/sample_data/repos.json +0 -1142
- package/dist/composables/keyboard.js +0 -473
- package/dist/composables/visibility/index.js +0 -47
- package/dist/index.js +0 -8
- package/dist/naming-conventions.js +0 -135
- package/dist/sql-introspection.js +0 -260
- package/dist/src/naming-conventions.d.ts +0 -107
- package/dist/src/naming-conventions.d.ts.map +0 -1
- package/dist/src/naming-conventions.js +0 -135
- package/dist/src/sql-introspection.d.ts +0 -111
- package/dist/src/sql-introspection.d.ts.map +0 -1
- package/dist/src/sql-introspection.js +0 -260
- package/dist/src/tsdoc-metadata.json +0 -11
- package/dist/src/workflow-scaffolder.d.ts +0 -77
- package/dist/src/workflow-scaffolder.d.ts.map +0 -1
- package/dist/src/workflow-scaffolder.js +0 -194
- package/dist/tests/setup.d.ts +0 -2
- package/dist/tests/setup.d.ts.map +0 -1
- package/dist/tests/setup.js +0 -4
- package/dist/types/index.js +0 -0
- package/dist/utilities.umd.cjs +0 -2
- package/dist/utilities.umd.cjs.map +0 -1
- package/dist/workflow-scaffolder.js +0 -194
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQL Schema Introspection Utilities
|
|
3
|
-
* Parses PostgreSQL DDL and converts to Stonecrop schema format
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Maps PostgreSQL data types to Stonecrop field types
|
|
8
|
-
* @param sqlType - PostgreSQL data type
|
|
9
|
-
* @param enumValues - Enum values if type is ENUM
|
|
10
|
-
* @returns Corresponding Stonecrop field type
|
|
11
|
-
* @public
|
|
12
|
-
*/
|
|
13
|
-
export function mapSQLTypeToFieldType(sqlType, enumValues) {
|
|
14
|
-
const normalizedType = sqlType.toLowerCase().trim();
|
|
15
|
-
// Handle parameterized types (e.g., varchar(255) -> varchar)
|
|
16
|
-
const baseType = normalizedType.split('(')[0].trim();
|
|
17
|
-
// Enum types become Select fields
|
|
18
|
-
if (enumValues && enumValues.length > 0) {
|
|
19
|
-
return 'Select';
|
|
20
|
-
}
|
|
21
|
-
// Type mapping based on PostgreSQL types
|
|
22
|
-
const typeMap = {
|
|
23
|
-
// Text types
|
|
24
|
-
varchar: 'Data',
|
|
25
|
-
character: 'Data',
|
|
26
|
-
char: 'Data',
|
|
27
|
-
text: 'Text',
|
|
28
|
-
// Numeric types
|
|
29
|
-
integer: 'Int',
|
|
30
|
-
int: 'Int',
|
|
31
|
-
int2: 'Int',
|
|
32
|
-
int4: 'Int',
|
|
33
|
-
int8: 'Int',
|
|
34
|
-
smallint: 'Int',
|
|
35
|
-
bigint: 'Int',
|
|
36
|
-
serial: 'Int',
|
|
37
|
-
bigserial: 'Int',
|
|
38
|
-
// Float types
|
|
39
|
-
numeric: 'Float',
|
|
40
|
-
decimal: 'Float',
|
|
41
|
-
real: 'Float',
|
|
42
|
-
float: 'Float',
|
|
43
|
-
float4: 'Float',
|
|
44
|
-
float8: 'Float',
|
|
45
|
-
double: 'Float',
|
|
46
|
-
money: 'Currency',
|
|
47
|
-
// Boolean
|
|
48
|
-
boolean: 'Check',
|
|
49
|
-
bool: 'Check',
|
|
50
|
-
// Date/Time types
|
|
51
|
-
timestamp: 'Datetime',
|
|
52
|
-
timestamptz: 'Datetime',
|
|
53
|
-
date: 'Date',
|
|
54
|
-
time: 'Time',
|
|
55
|
-
timetz: 'Time',
|
|
56
|
-
// JSON types (use Code for now)
|
|
57
|
-
json: 'Code',
|
|
58
|
-
jsonb: 'Code',
|
|
59
|
-
xml: 'Code',
|
|
60
|
-
// Array types (use Text for serialization)
|
|
61
|
-
array: 'Text',
|
|
62
|
-
};
|
|
63
|
-
return typeMap[baseType] || 'Data';
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Parses a PostgreSQL CREATE TABLE statement
|
|
67
|
-
* @param ddl - SQL DDL string
|
|
68
|
-
* @returns Array of parsed tables
|
|
69
|
-
* @public
|
|
70
|
-
*/
|
|
71
|
-
export function parseDDL(ddl) {
|
|
72
|
-
const tables = [];
|
|
73
|
-
// Split by CREATE TABLE statements
|
|
74
|
-
const createTableRegex = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:(\w+)\.)?(\w+)\s*\(([\s\S]*?)\);/gi;
|
|
75
|
-
let match;
|
|
76
|
-
while ((match = createTableRegex.exec(ddl)) !== null) {
|
|
77
|
-
const schema = match[1];
|
|
78
|
-
const tableName = match[2];
|
|
79
|
-
const columnsDef = match[3];
|
|
80
|
-
const table = {
|
|
81
|
-
name: tableName,
|
|
82
|
-
columns: [],
|
|
83
|
-
schema: schema || 'public',
|
|
84
|
-
};
|
|
85
|
-
// Parse columns and constraints
|
|
86
|
-
const lines = columnsDef.split(',').map(l => l.trim());
|
|
87
|
-
const primaryKeys = [];
|
|
88
|
-
const foreignKeys = new Map();
|
|
89
|
-
const uniqueColumns = new Set();
|
|
90
|
-
for (const line of lines) {
|
|
91
|
-
// Skip empty lines
|
|
92
|
-
if (!line)
|
|
93
|
-
continue;
|
|
94
|
-
// Handle PRIMARY KEY constraint
|
|
95
|
-
if (/PRIMARY\s+KEY/i.test(line)) {
|
|
96
|
-
const pkMatch = line.match(/PRIMARY\s+KEY\s*\(([^)]+)\)/i);
|
|
97
|
-
if (pkMatch) {
|
|
98
|
-
const cols = pkMatch[1].split(',').map(c => c.trim().replace(/"/g, ''));
|
|
99
|
-
primaryKeys.push(...cols);
|
|
100
|
-
}
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
// Handle FOREIGN KEY constraint
|
|
104
|
-
if (/FOREIGN\s+KEY/i.test(line)) {
|
|
105
|
-
const fkMatch = line.match(/FOREIGN\s+KEY\s*\(([^)]+)\)\s*REFERENCES\s+(\w+)\s*\(([^)]+)\)/i);
|
|
106
|
-
if (fkMatch) {
|
|
107
|
-
const localCol = fkMatch[1].trim().replace(/"/g, '');
|
|
108
|
-
const refTable = fkMatch[2].trim();
|
|
109
|
-
const refCol = fkMatch[3].trim().replace(/"/g, '');
|
|
110
|
-
foreignKeys.set(localCol, { table: refTable, column: refCol });
|
|
111
|
-
}
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
// Handle UNIQUE constraint
|
|
115
|
-
if (/UNIQUE/i.test(line) && /UNIQUE\s*\(/i.test(line)) {
|
|
116
|
-
const uniqueMatch = line.match(/UNIQUE\s*\(([^)]+)\)/i);
|
|
117
|
-
if (uniqueMatch) {
|
|
118
|
-
const cols = uniqueMatch[1].split(',').map(c => c.trim().replace(/"/g, ''));
|
|
119
|
-
cols.forEach(c => uniqueColumns.add(c));
|
|
120
|
-
}
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
// Parse column definition
|
|
124
|
-
const columnMatch = line.match(/^(\w+)\s+([A-Za-z0-9_()]+)(.*)$/i);
|
|
125
|
-
if (columnMatch) {
|
|
126
|
-
const columnName = columnMatch[1];
|
|
127
|
-
const dataType = columnMatch[2];
|
|
128
|
-
const modifiers = columnMatch[3] || '';
|
|
129
|
-
const column = {
|
|
130
|
-
name: columnName,
|
|
131
|
-
sqlType: dataType,
|
|
132
|
-
notNull: /NOT\s+NULL/i.test(modifiers),
|
|
133
|
-
primaryKey: false,
|
|
134
|
-
unique: /UNIQUE/i.test(modifiers),
|
|
135
|
-
};
|
|
136
|
-
// Extract enum values
|
|
137
|
-
if (/^ENUM/i.test(dataType)) {
|
|
138
|
-
const enumMatch = dataType.match(/ENUM\s*\(([^)]+)\)/i);
|
|
139
|
-
if (enumMatch) {
|
|
140
|
-
column.enumValues = enumMatch[1].split(',').map(v => v.trim().replace(/^'|'$/g, ''));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// Extract default value
|
|
144
|
-
const defaultMatch = modifiers.match(/DEFAULT\s+([^,\s]+)/i);
|
|
145
|
-
if (defaultMatch) {
|
|
146
|
-
column.defaultValue = defaultMatch[1];
|
|
147
|
-
}
|
|
148
|
-
// Extract CHECK constraint
|
|
149
|
-
const checkMatch = modifiers.match(/CHECK\s*\(([^)]+)\)/i);
|
|
150
|
-
if (checkMatch) {
|
|
151
|
-
column.checkConstraint = checkMatch[1];
|
|
152
|
-
}
|
|
153
|
-
// Check for inline REFERENCES (foreign key)
|
|
154
|
-
const refMatch = modifiers.match(/REFERENCES\s+(\w+)\s*\(([^)]+)\)/i);
|
|
155
|
-
if (refMatch) {
|
|
156
|
-
column.foreignKey = {
|
|
157
|
-
table: refMatch[1],
|
|
158
|
-
column: refMatch[2].trim().replace(/"/g, ''),
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
table.columns.push(column);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
// Apply primary keys
|
|
165
|
-
primaryKeys.forEach(pk => {
|
|
166
|
-
const col = table.columns.find(c => c.name === pk);
|
|
167
|
-
if (col) {
|
|
168
|
-
col.primaryKey = true;
|
|
169
|
-
col.notNull = true; // PKs are always NOT NULL
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
// Apply foreign keys
|
|
173
|
-
foreignKeys.forEach((fk, colName) => {
|
|
174
|
-
const col = table.columns.find(c => c.name === colName);
|
|
175
|
-
if (col) {
|
|
176
|
-
col.foreignKey = fk;
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
// Apply unique constraints
|
|
180
|
-
uniqueColumns.forEach(colName => {
|
|
181
|
-
const col = table.columns.find(c => c.name === colName);
|
|
182
|
-
if (col) {
|
|
183
|
-
col.unique = true;
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
tables.push(table);
|
|
187
|
-
}
|
|
188
|
-
return tables;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Converts SQL table to Stonecrop schema format
|
|
192
|
-
* @param table - Parsed SQL table
|
|
193
|
-
* @param namingConverter - Function to convert field names
|
|
194
|
-
* @returns Conversion result with schema and relationships
|
|
195
|
-
* @public
|
|
196
|
-
*/
|
|
197
|
-
export function convertTableToSchema(table, namingConverter) {
|
|
198
|
-
const result = {
|
|
199
|
-
doctype: table.name,
|
|
200
|
-
schema: [],
|
|
201
|
-
relationships: [],
|
|
202
|
-
};
|
|
203
|
-
for (const column of table.columns) {
|
|
204
|
-
const { fieldname, label } = namingConverter
|
|
205
|
-
? namingConverter(column.name)
|
|
206
|
-
: { fieldname: column.name, label: column.name };
|
|
207
|
-
const fieldtype = column.foreignKey ? 'Link' : mapSQLTypeToFieldType(column.sqlType, column.enumValues);
|
|
208
|
-
const field = {
|
|
209
|
-
fieldname,
|
|
210
|
-
label,
|
|
211
|
-
fieldtype,
|
|
212
|
-
};
|
|
213
|
-
// Add required flag for NOT NULL columns (except PKs which are readonly)
|
|
214
|
-
if (column.notNull && !column.primaryKey) {
|
|
215
|
-
field.required = true;
|
|
216
|
-
}
|
|
217
|
-
// Make primary keys readonly
|
|
218
|
-
if (column.primaryKey) {
|
|
219
|
-
field.readonly = true;
|
|
220
|
-
}
|
|
221
|
-
// Handle foreign keys (Link fields)
|
|
222
|
-
if (column.foreignKey) {
|
|
223
|
-
field.options = column.foreignKey.table;
|
|
224
|
-
result.relationships.push({
|
|
225
|
-
fieldname,
|
|
226
|
-
targetDoctype: column.foreignKey.table,
|
|
227
|
-
targetField: column.foreignKey.column,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
// Handle enum values (Select fields)
|
|
231
|
-
if (column.enumValues && column.enumValues.length > 0) {
|
|
232
|
-
field.options = column.enumValues.join('\n');
|
|
233
|
-
}
|
|
234
|
-
// Handle default values
|
|
235
|
-
if (column.defaultValue) {
|
|
236
|
-
// Clean up default value (remove quotes, casts, etc.)
|
|
237
|
-
let defaultVal = column.defaultValue.replace(/^'|'$/g, '');
|
|
238
|
-
if (fieldtype === 'Int' || fieldtype === 'Float') {
|
|
239
|
-
defaultVal = parseFloat(defaultVal);
|
|
240
|
-
}
|
|
241
|
-
else if (fieldtype === 'Check') {
|
|
242
|
-
defaultVal = defaultVal === 'true' || defaultVal === 't';
|
|
243
|
-
}
|
|
244
|
-
field.default = defaultVal;
|
|
245
|
-
}
|
|
246
|
-
result.schema.push(field);
|
|
247
|
-
}
|
|
248
|
-
return result;
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Introspects SQL DDL and converts all tables to Stonecrop schemas
|
|
252
|
-
* @param ddl - PostgreSQL DDL string
|
|
253
|
-
* @param namingConverter - Optional function to convert field names
|
|
254
|
-
* @returns Array of conversion results
|
|
255
|
-
* @public
|
|
256
|
-
*/
|
|
257
|
-
export function introspectSQL(ddl, namingConverter) {
|
|
258
|
-
const tables = parseDDL(ddl);
|
|
259
|
-
return tables.map(table => convertTableToSchema(table, namingConverter));
|
|
260
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// This file is read by tools that parse documentation comments conforming to the TSDoc standard.
|
|
2
|
-
// It should be published with your NPM package. It should not be tracked by Git.
|
|
3
|
-
{
|
|
4
|
-
"tsdocVersion": "0.12",
|
|
5
|
-
"toolPackages": [
|
|
6
|
-
{
|
|
7
|
-
"packageName": "@microsoft/api-extractor",
|
|
8
|
-
"packageVersion": "7.57.0"
|
|
9
|
-
}
|
|
10
|
-
]
|
|
11
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workflow Scaffolding Utilities
|
|
3
|
-
* Generates XState workflow configurations from SQL schema patterns
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
import type { AnyStateNodeConfig } from 'xstate';
|
|
7
|
-
import type { SQLColumn, SQLTable } from './sql-introspection';
|
|
8
|
-
/**
|
|
9
|
-
* Workflow scaffolding result
|
|
10
|
-
* @public
|
|
11
|
-
*/
|
|
12
|
-
export interface WorkflowScaffold {
|
|
13
|
-
/** Generated XState machine configuration */
|
|
14
|
-
workflow: AnyStateNodeConfig;
|
|
15
|
-
/** Actions map with stubbed action names */
|
|
16
|
-
actions: Record<string, string[]>;
|
|
17
|
-
/** Source column that workflow was generated from */
|
|
18
|
-
sourceColumn?: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Detects status/state columns in SQL table
|
|
22
|
-
* @param table - Parsed SQL table
|
|
23
|
-
* @returns Status column if found
|
|
24
|
-
* @public
|
|
25
|
-
*/
|
|
26
|
-
export declare function detectStatusColumn(table: SQLTable): SQLColumn | undefined;
|
|
27
|
-
/**
|
|
28
|
-
* Extracts state values from status column
|
|
29
|
-
* @param column - Status column
|
|
30
|
-
* @returns Array of state names
|
|
31
|
-
* @public
|
|
32
|
-
*/
|
|
33
|
-
export declare function extractStateValues(column: SQLColumn): string[];
|
|
34
|
-
/**
|
|
35
|
-
* Generates transition event names from state pairs
|
|
36
|
-
* @param fromState - Source state
|
|
37
|
-
* @param toState - Target state
|
|
38
|
-
* @returns Uppercase transition event name
|
|
39
|
-
* @public
|
|
40
|
-
* @example
|
|
41
|
-
* ```typescript
|
|
42
|
-
* generateTransitionName('draft', 'pending') // 'SUBMIT'
|
|
43
|
-
* generateTransitionName('pending', 'approved') // 'APPROVE'
|
|
44
|
-
* generateTransitionName('approved', 'rejected') // 'REJECT'
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export declare function generateTransitionName(fromState: string, toState: string): string;
|
|
48
|
-
/**
|
|
49
|
-
* Scaffolds a basic workflow from detected states
|
|
50
|
-
* @param states - Array of state names
|
|
51
|
-
* @param machineId - Machine identifier
|
|
52
|
-
* @returns Workflow scaffold with machine config and actions
|
|
53
|
-
* @public
|
|
54
|
-
*/
|
|
55
|
-
export declare function scaffoldWorkflow(states: string[], machineId: string): WorkflowScaffold;
|
|
56
|
-
/**
|
|
57
|
-
* Scaffolds workflow from SQL table
|
|
58
|
-
* @param table - Parsed SQL table
|
|
59
|
-
* @param machineId - Optional machine ID (defaults to table name)
|
|
60
|
-
* @returns Workflow scaffold or undefined if no status column found
|
|
61
|
-
* @public
|
|
62
|
-
*/
|
|
63
|
-
export declare function scaffoldWorkflowFromTable(table: SQLTable, machineId?: string): WorkflowScaffold | undefined;
|
|
64
|
-
/**
|
|
65
|
-
* Generates default workflow layout positions for visual editor
|
|
66
|
-
* @param states - Array of state names
|
|
67
|
-
* @param horizontal - Whether to layout horizontally (default) or vertically
|
|
68
|
-
* @returns Layout object with position coordinates
|
|
69
|
-
* @public
|
|
70
|
-
*/
|
|
71
|
-
export declare function generateWorkflowLayout(states: string[], horizontal?: boolean): Record<string, {
|
|
72
|
-
position: {
|
|
73
|
-
x: number;
|
|
74
|
-
y: number;
|
|
75
|
-
};
|
|
76
|
-
}>;
|
|
77
|
-
//# sourceMappingURL=workflow-scaffolder.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-scaffolder.d.ts","sourceRoot":"","sources":["../../src/workflow-scaffolder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAE9D;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,6CAA6C;IAC7C,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACjC,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAgBzE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,CA0B9D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CA+BjF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAgEtF;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAa3G;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,MAAM,EAAE,EAChB,UAAU,GAAE,OAAc,GACxB,MAAM,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CAcxD"}
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workflow Scaffolding Utilities
|
|
3
|
-
* Generates XState workflow configurations from SQL schema patterns
|
|
4
|
-
* @packageDocumentation
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Detects status/state columns in SQL table
|
|
8
|
-
* @param table - Parsed SQL table
|
|
9
|
-
* @returns Status column if found
|
|
10
|
-
* @public
|
|
11
|
-
*/
|
|
12
|
-
export function detectStatusColumn(table) {
|
|
13
|
-
// Look for columns matching common status patterns
|
|
14
|
-
const statusPatterns = [/^status$/i, /^state$/i, /^workflow_state$/i, /^document_status$/i, /_status$/i, /_state$/i];
|
|
15
|
-
for (const column of table.columns) {
|
|
16
|
-
for (const pattern of statusPatterns) {
|
|
17
|
-
if (pattern.test(column.name)) {
|
|
18
|
-
// Check if it's an enum type or has check constraints
|
|
19
|
-
if (column.enumValues || column.checkConstraint) {
|
|
20
|
-
return column;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return undefined;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Extracts state values from status column
|
|
29
|
-
* @param column - Status column
|
|
30
|
-
* @returns Array of state names
|
|
31
|
-
* @public
|
|
32
|
-
*/
|
|
33
|
-
export function extractStateValues(column) {
|
|
34
|
-
// Use enum values if available
|
|
35
|
-
if (column.enumValues && column.enumValues.length > 0) {
|
|
36
|
-
return column.enumValues;
|
|
37
|
-
}
|
|
38
|
-
// Try to extract from CHECK constraint
|
|
39
|
-
if (column.checkConstraint) {
|
|
40
|
-
// Match patterns like: status IN ('draft', 'pending', 'approved')
|
|
41
|
-
const inMatch = column.checkConstraint.match(/IN\s*\(([^)]+)\)/i);
|
|
42
|
-
if (inMatch) {
|
|
43
|
-
return inMatch[1]
|
|
44
|
-
.split(',')
|
|
45
|
-
.map(v => v.trim().replace(/^'|'$/g, ''))
|
|
46
|
-
.filter(Boolean);
|
|
47
|
-
}
|
|
48
|
-
// Match patterns like: status = 'draft' OR status = 'pending' OR status = 'approved'
|
|
49
|
-
const orMatch = column.checkConstraint.match(/'([^']+)'/g);
|
|
50
|
-
if (orMatch) {
|
|
51
|
-
return orMatch.map(v => v.replace(/'/g, '')).filter(Boolean);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Return common defaults if no values detected
|
|
55
|
-
return ['draft', 'pending', 'approved', 'rejected'];
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Generates transition event names from state pairs
|
|
59
|
-
* @param fromState - Source state
|
|
60
|
-
* @param toState - Target state
|
|
61
|
-
* @returns Uppercase transition event name
|
|
62
|
-
* @public
|
|
63
|
-
* @example
|
|
64
|
-
* ```typescript
|
|
65
|
-
* generateTransitionName('draft', 'pending') // 'SUBMIT'
|
|
66
|
-
* generateTransitionName('pending', 'approved') // 'APPROVE'
|
|
67
|
-
* generateTransitionName('approved', 'rejected') // 'REJECT'
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
export function generateTransitionName(fromState, toState) {
|
|
71
|
-
// Common transition patterns
|
|
72
|
-
const transitionMap = {
|
|
73
|
-
'draft->pending': 'SUBMIT',
|
|
74
|
-
'draft->submitted': 'SUBMIT',
|
|
75
|
-
'pending->approved': 'APPROVE',
|
|
76
|
-
'pending->accepted': 'APPROVE',
|
|
77
|
-
'submitted->approved': 'APPROVE',
|
|
78
|
-
'pending->rejected': 'REJECT',
|
|
79
|
-
'submitted->rejected': 'REJECT',
|
|
80
|
-
'approved->rejected': 'REJECT',
|
|
81
|
-
'approved->closed': 'CLOSE',
|
|
82
|
-
'rejected->draft': 'REOPEN',
|
|
83
|
-
'closed->draft': 'REOPEN',
|
|
84
|
-
'active->inactive': 'DEACTIVATE',
|
|
85
|
-
'inactive->active': 'ACTIVATE',
|
|
86
|
-
'new->draft': 'SAVE',
|
|
87
|
-
};
|
|
88
|
-
const key = `${fromState.toLowerCase()}->${toState.toLowerCase()}`;
|
|
89
|
-
if (transitionMap[key]) {
|
|
90
|
-
return transitionMap[key];
|
|
91
|
-
}
|
|
92
|
-
// Generate uppercase event name from target state
|
|
93
|
-
// e.g., 'approved' -> 'APPROVE', 'completed' -> 'COMPLETE'
|
|
94
|
-
const verb = toState.toUpperCase();
|
|
95
|
-
if (verb.endsWith('ED')) {
|
|
96
|
-
return verb.slice(0, -1); // 'APPROVED' -> 'APPROVE'
|
|
97
|
-
}
|
|
98
|
-
return verb;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Scaffolds a basic workflow from detected states
|
|
102
|
-
* @param states - Array of state names
|
|
103
|
-
* @param machineId - Machine identifier
|
|
104
|
-
* @returns Workflow scaffold with machine config and actions
|
|
105
|
-
* @public
|
|
106
|
-
*/
|
|
107
|
-
export function scaffoldWorkflow(states, machineId) {
|
|
108
|
-
if (states.length === 0) {
|
|
109
|
-
throw new Error('Cannot scaffold workflow: no states provided');
|
|
110
|
-
}
|
|
111
|
-
// Capitalize state names for consistency
|
|
112
|
-
const capitalizedStates = states.map(s => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase());
|
|
113
|
-
// Use first state as initial
|
|
114
|
-
const initialState = capitalizedStates[0];
|
|
115
|
-
// Build state machine configuration
|
|
116
|
-
const stateConfig = {};
|
|
117
|
-
const actions = {};
|
|
118
|
-
for (let i = 0; i < capitalizedStates.length; i++) {
|
|
119
|
-
const currentState = capitalizedStates[i];
|
|
120
|
-
stateConfig[currentState] = { on: {} };
|
|
121
|
-
// Create transitions to subsequent states
|
|
122
|
-
for (let j = i + 1; j < capitalizedStates.length; j++) {
|
|
123
|
-
const targetState = capitalizedStates[j];
|
|
124
|
-
const eventName = generateTransitionName(currentState, targetState);
|
|
125
|
-
stateConfig[currentState].on[eventName] = targetState;
|
|
126
|
-
// Add stub action for this transition
|
|
127
|
-
if (!actions[eventName]) {
|
|
128
|
-
actions[eventName] = [];
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
// Add a CANCEL transition back to first state (except from first state)
|
|
132
|
-
if (i > 0 && currentState !== initialState) {
|
|
133
|
-
stateConfig[currentState].on['CANCEL'] = initialState;
|
|
134
|
-
if (!actions['CANCEL']) {
|
|
135
|
-
actions['CANCEL'] = [];
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// Mark last state as final (optional)
|
|
140
|
-
const lastState = capitalizedStates[capitalizedStates.length - 1];
|
|
141
|
-
if (lastState.toLowerCase().includes('close') ||
|
|
142
|
-
lastState.toLowerCase().includes('reject') ||
|
|
143
|
-
lastState.toLowerCase().includes('complet')) {
|
|
144
|
-
stateConfig[lastState].type = 'final';
|
|
145
|
-
}
|
|
146
|
-
const workflow = {
|
|
147
|
-
id: machineId,
|
|
148
|
-
initial: initialState,
|
|
149
|
-
states: stateConfig,
|
|
150
|
-
};
|
|
151
|
-
return {
|
|
152
|
-
workflow,
|
|
153
|
-
actions,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Scaffolds workflow from SQL table
|
|
158
|
-
* @param table - Parsed SQL table
|
|
159
|
-
* @param machineId - Optional machine ID (defaults to table name)
|
|
160
|
-
* @returns Workflow scaffold or undefined if no status column found
|
|
161
|
-
* @public
|
|
162
|
-
*/
|
|
163
|
-
export function scaffoldWorkflowFromTable(table, machineId) {
|
|
164
|
-
const statusColumn = detectStatusColumn(table);
|
|
165
|
-
if (!statusColumn) {
|
|
166
|
-
return undefined;
|
|
167
|
-
}
|
|
168
|
-
const states = extractStateValues(statusColumn);
|
|
169
|
-
const scaffold = scaffoldWorkflow(states, machineId || table.name);
|
|
170
|
-
return {
|
|
171
|
-
...scaffold,
|
|
172
|
-
sourceColumn: statusColumn.name,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Generates default workflow layout positions for visual editor
|
|
177
|
-
* @param states - Array of state names
|
|
178
|
-
* @param horizontal - Whether to layout horizontally (default) or vertically
|
|
179
|
-
* @returns Layout object with position coordinates
|
|
180
|
-
* @public
|
|
181
|
-
*/
|
|
182
|
-
export function generateWorkflowLayout(states, horizontal = true) {
|
|
183
|
-
const layout = {};
|
|
184
|
-
const spacing = horizontal ? 250 : 100;
|
|
185
|
-
const offsetX = 50;
|
|
186
|
-
const offsetY = 50;
|
|
187
|
-
for (let i = 0; i < states.length; i++) {
|
|
188
|
-
const state = states[i].charAt(0).toUpperCase() + states[i].slice(1).toLowerCase();
|
|
189
|
-
layout[state] = {
|
|
190
|
-
position: horizontal ? { x: offsetX + i * spacing, y: offsetY } : { x: offsetX, y: offsetY + i * spacing },
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
return layout;
|
|
194
|
-
}
|
package/dist/tests/setup.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../tests/setup.ts"],"names":[],"mappings":""}
|
package/dist/tests/setup.js
DELETED
package/dist/types/index.js
DELETED
|
File without changes
|
package/dist/utilities.umd.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
(function(g,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],f):(g=typeof globalThis<"u"?globalThis:g||self,f(g["@stonecrop/utilities"]={},g.Vue))})(this,(function(g,f){"use strict";function H(e,t){return f.getCurrentScope()?(f.onScopeDispose(e,t),!0):!1}const N=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const K=e=>e!=null,W=Object.prototype.toString,F=e=>W.call(e)==="[object Object]",_=()=>{};function y(e){return Array.isArray(e)?e:[e]}function j(e,t,n){return f.watch(e,t,{...n,immediate:!0})}const w=N?window:void 0;function C(e){var t;const n=f.toValue(e);return(t=n?.$el)!==null&&t!==void 0?t:n}function v(...e){const t=(o,s,r,l)=>(o.addEventListener(s,r,l),()=>o.removeEventListener(s,r,l)),n=f.computed(()=>{const o=y(f.toValue(e[0])).filter(s=>s!=null);return o.every(s=>typeof s!="string")?o:void 0});return j(()=>{var o,s;return[(o=(s=n.value)===null||s===void 0?void 0:s.map(r=>C(r)))!==null&&o!==void 0?o:[w].filter(r=>r!=null),y(f.toValue(n.value?e[1]:e[0])),y(f.unref(n.value?e[2]:e[1])),f.toValue(n.value?e[3]:e[2])]},([o,s,r,l],i,a)=>{if(!o?.length||!s?.length||!r?.length)return;const c=F(l)?{...l}:l,u=o.flatMap(d=>s.flatMap(p=>r.map(m=>t(d,p,m,c))));a(()=>{u.forEach(d=>d())})},{flush:"post"})}function B(){const e=f.shallowRef(!1),t=f.getCurrentInstance();return t&&f.onMounted(()=>{e.value=!0},t),e}function q(e){const t=B();return f.computed(()=>(t.value,!!e()))}function G(e,t,n={}){const{window:o=w,...s}=n;let r;const l=q(()=>o&&"MutationObserver"in o),i=()=>{r&&(r.disconnect(),r=void 0)},a=f.watch(f.computed(()=>{const d=y(f.toValue(e)).map(C).filter(K);return new Set(d)}),d=>{i(),l.value&&d.size&&(r=new MutationObserver(t),d.forEach(p=>r.observe(p,s)))},{immediate:!0,flush:"post"}),c=()=>r?.takeRecords(),u=()=>{a(),i()};return H(u),{isSupported:l,stop:u,takeRecords:c}}function z(e,t,n={}){const{window:o=w,document:s=o?.document,flush:r="sync"}=n;if(!o||!s)return _;let l;const i=u=>{l?.(),l=u},a=f.watchEffect(()=>{const u=C(e);if(u){const{stop:d}=G(s,p=>{p.map(m=>[...m.removedNodes]).flat().some(m=>m===u||m.contains(u))&&t(p)},{window:o,childList:!0,subtree:!0});i(d)}},{flush:r}),c=()=>{a(),i()};return H(c),c}function J(e={}){var t;const{window:n=w,deep:o=!0,triggerOnRemoval:s=!1}=e,r=(t=e.document)!==null&&t!==void 0?t:n?.document,l=()=>{let c=r?.activeElement;if(o)for(var u;c?.shadowRoot;)c=c==null||(u=c.shadowRoot)===null||u===void 0?void 0:u.activeElement;return c},i=f.shallowRef(),a=()=>{i.value=l()};if(n){const c={capture:!0,passive:!0};v(n,"blur",u=>{u.relatedTarget===null&&a()},c),v(n,"focus",a,c)}return s&&z(i,a,{document:r}),a(),i}const Q="focusin",X="focusout",Y=":focus-within";function Z(e,t={}){const{window:n=w}=t,o=f.computed(()=>C(e)),s=f.shallowRef(!1),r=f.computed(()=>s.value);if(!n||!J(t).value)return{focused:r};const i={passive:!0};return v(o,Q,()=>s.value=!0,i),v(o,X,()=>{var a,c,u;return s.value=(a=(c=o.value)===null||c===void 0||(u=c.matches)===null||u===void 0?void 0:u.call(c,Y))!==null&&a!==void 0?a:!1},i),{focused:r}}function ee(e,{window:t=w,scrollTarget:n}={}){const o=f.ref(!1),s=()=>{if(!t)return;const r=t.document,l=C(e);if(!l)o.value=!1;else{const i=l.getBoundingClientRect();o.value=i.top<=(t.innerHeight||r.documentElement.clientHeight)&&i.left<=(t.innerWidth||r.documentElement.clientWidth)&&i.bottom>=0&&i.right>=0}};return f.watch(()=>C(e),()=>s(),{immediate:!0,flush:"post"}),t&&v(n||t,"scroll",s,{capture:!1,passive:!0}),o}const $=e=>{let t=ee(e).value;return t=t&&e.offsetHeight>0,t},E=e=>e.tabIndex>=0,L=e=>{const t=e.target;return T(t)},T=e=>{let t;if(e instanceof HTMLTableCellElement){const n=e.parentElement?.previousElementSibling;if(n){const s=Array.from(n.children)[e.cellIndex];s&&(t=s)}}else if(e instanceof HTMLTableRowElement){const n=e.previousElementSibling;n&&(t=n)}return t&&(!E(t)||!$(t))?T(t):t},te=e=>{const t=e.target;let n;if(t instanceof HTMLTableCellElement){const o=t.parentElement?.parentElement;if(o){const r=o.firstElementChild?.children[t.cellIndex];r&&(n=r)}}else if(t instanceof HTMLTableRowElement){const o=t.parentElement;if(o){const s=o.firstElementChild;s&&(n=s)}}return n&&(!E(n)||!$(n))?S(n):n},x=e=>{const t=e.target;return S(t)},S=e=>{let t;if(e instanceof HTMLTableCellElement){const n=e.parentElement?.nextElementSibling;if(n){const s=Array.from(n.children)[e.cellIndex];s&&(t=s)}}else if(e instanceof HTMLTableRowElement){const n=e.nextElementSibling;n&&(t=n)}return t&&(!E(t)||!$(t))?S(t):t},ne=e=>{const t=e.target;let n;if(t instanceof HTMLTableCellElement){const o=t.parentElement?.parentElement;if(o){const r=o.lastElementChild?.children[t.cellIndex];r&&(n=r)}}else if(t instanceof HTMLTableRowElement){const o=t.parentElement;if(o){const s=o.lastElementChild;s&&(n=s)}}return n&&(!E(n)||!$(n))?T(n):n},P=e=>{const t=e.target;return R(t)},R=e=>{let t;return e.previousElementSibling?t=e.previousElementSibling:t=e.parentElement?.previousElementSibling?.lastElementChild,t&&(!E(t)||!$(t))?R(t):t},D=e=>{const t=e.target;return M(t)},M=e=>{let t;return e.nextElementSibling?t=e.nextElementSibling:t=e.parentElement?.nextElementSibling?.firstElementChild,t&&(!E(t)||!$(t))?M(t):t},O=e=>{const o=e.target.parentElement?.firstElementChild;return o&&(!E(o)||!$(o))?M(o):o},U=e=>{const o=e.target.parentElement?.lastElementChild;return o&&(!E(o)||!$(o))?R(o):o},b=["alt","control","shift","meta"],oe={ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right"},V={"keydown.up":e=>{const t=L(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.down":e=>{const t=x(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.left":e=>{const t=P(e);e.preventDefault(),e.stopPropagation(),t&&t.focus()},"keydown.right":e=>{const t=D(e);e.preventDefault(),e.stopPropagation(),t&&t.focus()},"keydown.control.up":e=>{const t=te(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.control.down":e=>{const t=ne(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.control.left":e=>{const t=O(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.control.right":e=>{const t=U(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.end":e=>{const t=U(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.enter":e=>{if(e.target instanceof HTMLTableCellElement){e.preventDefault(),e.stopPropagation();const n=x(e);n&&n.focus()}},"keydown.shift.enter":e=>{if(e.target instanceof HTMLTableCellElement){e.preventDefault(),e.stopPropagation();const n=L(e);n&&n.focus()}},"keydown.home":e=>{const t=O(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.tab":e=>{const t=D(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())},"keydown.shift.tab":e=>{const t=P(e);t&&(e.preventDefault(),e.stopPropagation(),t.focus())}};function le(e){const t=l=>{let i=null;return l.parent&&(typeof l.parent=="string"?i=document.querySelector(l.parent):l.parent instanceof HTMLElement?i=l.parent:i=l.parent.value),i},n=l=>{const i=t(l);let a=[];if(typeof l.selectors=="string")a=Array.from(i?i.querySelectorAll(l.selectors):document.querySelectorAll(l.selectors));else if(Array.isArray(l.selectors))for(const c of l.selectors)c instanceof HTMLElement?a.push(c):a.push(c.$el);else if(l.selectors instanceof HTMLElement)a.push(l.selectors);else if(l.selectors?.value)if(Array.isArray(l.selectors.value))for(const c of l.selectors.value)c instanceof HTMLElement?a.push(c):a.push(c.$el);else a.push(l.selectors.value);return a},o=l=>{const i=t(l);let a=[];return l.selectors?a=n(l):i&&(a=Array.from(i.children).filter(u=>E(u)&&$(u))),a},s=l=>i=>{const a=oe[i.key]||i.key.toLowerCase();if(b.includes(a))return;const c=l.handlers||V;for(const u of Object.keys(c)){const[d,...p]=u.split(".");if(d==="keydown"&&p.includes(a)){const m=c[u],k=p.filter(h=>b.includes(h)),I=b.some(h=>{const A=h.charAt(0).toUpperCase()+h.slice(1);return i.getModifierState(A)});if(k.length>0){if(I){for(const h of b)if(p.includes(h)){const A=h.charAt(0).toUpperCase()+h.slice(1);i.getModifierState(A)&&m(i)}}}else I||m(i)}}},r=[];f.onMounted(()=>{for(const l of e){const i=t(l),a=o(l),c=s(l),u=i?[i]:a;for(const d of u){const{focused:p}=Z(f.ref(d)),m=f.watch(p,k=>{k?d.addEventListener("keydown",c):d.removeEventListener("keydown",c)});r.push(m)}}}),f.onBeforeUnmount(()=>{for(const l of r)l()})}function se(e){}g.defaultKeypressHandlers=V,g.install=se,g.useKeyboardNav=le,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})}));
|
|
2
|
-
//# sourceMappingURL=utilities.umd.cjs.map
|