rune-lab 0.0.7 โ 0.0.8
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 +38 -25
- package/dist/components/dataview/RLMetadataTable.svelte +1 -4
- package/dist/components/explorer/RLSchemaExplorer.svelte +130 -118
- package/dist/components/form/RLFilterForm.svelte +1 -13
- package/dist/components/form/RLFunctionForm.svelte +2 -13
- package/dist/components/form/RLResourceForm.svelte +1 -13
- package/dist/components/stores/explorer.svelte.d.ts +56 -0
- package/dist/mod.d.ts +2 -0
- package/dist/mod.js +2 -0
- package/dist/tools/form-helpers.d.ts +23 -0
- package/dist/tools/form-helpers.js +51 -0
- package/dist/tools/schema-transformer.d.ts +5 -0
- package/dist/tools/schema-transformer.js +130 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<h1 align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/Yrrrrrf/rune-lab/main/static/rune.png" alt="Rune Lab Icon" width="128" height="128" description="
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Yrrrrrf/rune-lab/main/static/rune.png" alt="Rune Lab Icon" width="128" height="128" description="Icon representing the Svelte Runes system">
|
|
3
3
|
<div align="center">Rune Lab</div>
|
|
4
4
|
</h1>
|
|
5
5
|
|
|
@@ -14,48 +14,61 @@
|
|
|
14
14
|
|
|
15
15
|
## Overview
|
|
16
16
|
|
|
17
|
-
Rune Lab is
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
**Rune Lab** is your modern toolkit for crafting stunning, reactive web applications with
|
|
18
|
+
**Svelte 5**. Harnessing the power of Svelte's new **Runes** system, Rune Lab offers a suite of
|
|
19
|
+
elegant UI components designed for seamless data handling and beautiful theming.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
It's built to integrate effortlessly with your data sources, especially shining when connected
|
|
22
|
+
to the [prism-py](https://github.com/Yrrrrrf/prism-py) and
|
|
23
|
+
[prism-ts](https://github.com/Yrrrrrf/prism-ts) ecosystem for end-to-end type-safe API
|
|
24
|
+
interactions.
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
|
|
26
|
+
## Key Features
|
|
27
|
+
|
|
28
|
+
- **โจ Svelte 5 Runes Core:** Experience fine-grained reactivity and cleaner component logic.
|
|
29
|
+
- **๐จ Dynamic Theming:** Powered by DaisyUI & Tailwind CSS for extensive customization and
|
|
30
|
+
out-of-the-box themes.
|
|
31
|
+
- **๐ TypeScript First:** Robust type-safety for a confident and productive development
|
|
32
|
+
workflow.
|
|
33
|
+
- **๐ Data-Aware Components:** Tools and components built to handle and visualize complex data.
|
|
34
|
+
- **๐ฐ๏ธ Interactive Schema Explorer:** A standout feature! Visually explore and interact with
|
|
35
|
+
database schemas exposed by `prism-py` APIs directly within your Svelte application. Test CRUD
|
|
36
|
+
operations, execute functions, and understand your data structure like never before.
|
|
37
|
+
- **๐ Smart API Integration:** Includes `apiStore` (using `prism-ts`) for easy and type-safe
|
|
38
|
+
connection to backend APIs.
|
|
39
|
+
- **๐ฆ Lightweight Core:** Designed to be lean, with optional integrations.
|
|
40
|
+
- **๐ฆ Universal Access:** Available on JSR (for Deno) and NPM (for Node.js/Bun/Yarn).
|
|
41
|
+
|
|
42
|
+
## The Prism Ecosystem Advantage
|
|
43
|
+
|
|
44
|
+
Rune Lab is designed to be a perfect companion to the Prism ecosystem:
|
|
45
|
+
|
|
46
|
+
- **[prism-py](https://github.com/Yrrrrrf/prism-py):** Automatically generates REST APIs from
|
|
47
|
+
your database schema.
|
|
48
|
+
- **[prism-ts](https://github.com/Yrrrrrf/prism-ts):** A TypeScript client that consumes these
|
|
49
|
+
APIs with full type-safety.
|
|
50
|
+
|
|
51
|
+
When used together, Rune Lab's API integration tools (like the `apiStore` and
|
|
52
|
+
`RLSchemaExplorer`) provide a remarkably streamlined and type-safe path from your backend data
|
|
53
|
+
to your frontend UI.
|
|
30
54
|
|
|
31
55
|
## Installation
|
|
32
56
|
|
|
33
|
-
### Using Deno / JSR
|
|
57
|
+
### Using Deno / [JSR](https://jsr.io/@yrrrrrf/rune-lab)
|
|
34
58
|
|
|
35
59
|
```bash
|
|
36
60
|
# Add to your Deno project
|
|
37
61
|
deno add @yrrrrrf/rune-lab
|
|
38
62
|
```
|
|
39
63
|
|
|
40
|
-
### Using NPM / Bun / Yarn
|
|
64
|
+
### Using [NPM](https://www.npmjs.com/package/rune-lab) / Bun / Yarn
|
|
41
65
|
|
|
42
66
|
```bash
|
|
43
|
-
# NPM
|
|
44
67
|
npm install rune-lab
|
|
45
|
-
|
|
46
|
-
# Bun
|
|
47
68
|
bun add rune-lab
|
|
48
|
-
|
|
49
|
-
# Yarn
|
|
50
69
|
yarn add rune-lab
|
|
51
70
|
```
|
|
52
71
|
|
|
53
72
|
## License
|
|
54
73
|
|
|
55
74
|
MIT License - See [LICENSE](LICENSE) for details.
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
<div align="center">
|
|
60
|
-
Built with โค๏ธ using Svelte 5 and Deno
|
|
61
|
-
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<!-- src/lib/components/dataview/RLMetadataTable.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
3
|
import type { ColumnMetadata, ColumnReference, EnumMetadata } from '@yrrrrrf/prism-ts';
|
|
4
|
+
import { formatReferenceText } from '../../tools/form-helpers.js';
|
|
4
5
|
|
|
5
6
|
let {
|
|
6
7
|
title,
|
|
@@ -18,10 +19,6 @@
|
|
|
18
19
|
onEnumClick?: (enumData: { name: string; values: string[] }) => void; // Make optional
|
|
19
20
|
}>();
|
|
20
21
|
|
|
21
|
-
function formatReferenceText(ref: ColumnReference | undefined): string {
|
|
22
|
-
if (!ref) return '';
|
|
23
|
-
return `${ref.schema}.${ref.table}.${ref.column}`;
|
|
24
|
-
}
|
|
25
22
|
|
|
26
23
|
function handleFkButtonClick(ref: ColumnReference | undefined) {
|
|
27
24
|
if (ref && onFkClick) {
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
<!-- src/lib/components/explorer/RLSchemaExplorer.svelte -->
|
|
2
|
-
// src/lib/components/explorer/RLSchemaExplorer.svelte
|
|
3
2
|
<script lang="ts">
|
|
4
3
|
import type {
|
|
5
|
-
SchemaMetadata,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
FunctionMetadata,
|
|
10
|
-
ColumnMetadata,
|
|
11
|
-
ColumnReference,
|
|
12
|
-
FunctionParameter,
|
|
4
|
+
SchemaMetadata as PrismSchemaMetadata, // Original type from prism-ts
|
|
5
|
+
// Other prism-ts types might be needed if used directly for options in openModalForApi
|
|
6
|
+
ColumnMetadata as PrismColumnMetadata,
|
|
7
|
+
FunctionParameter as PrismFunctionParameter,
|
|
13
8
|
} from '@yrrrrrf/prism-ts';
|
|
14
9
|
import { apiStore } from '../stores/api.svelte';
|
|
15
10
|
import { explorerStore, type ExplorerEntityType as StoreExplorerEntityType } from '../stores/explorer.svelte';
|
|
@@ -17,14 +12,14 @@
|
|
|
17
12
|
import RLApiInterface from '../api/RLApiInterface.svelte';
|
|
18
13
|
import RLApiOperationModal from '../api/RLApiOperationModal.svelte';
|
|
19
14
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
// Import Rune Lab specific types and the transformer
|
|
16
|
+
import type { RLSchemaData, RLTableMetadata, RLViewMetadata, RLEnumMetadata, RLFunctionMetadata, RLColumnMetadata, RLColumnReference } from '../stores/explorer.svelte';
|
|
17
|
+
import { transformPrismSchemasToRLData } from '../../tools/schema-transformer.js'; // Correct path to tools index
|
|
23
18
|
|
|
24
19
|
type APIOperation = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
25
20
|
type ModalResourceType = 'table' | 'view' | 'function';
|
|
26
21
|
|
|
27
|
-
let schemas = $state<
|
|
22
|
+
let schemas = $state<RLSchemaData[] | null>(null); // Use RLSchemaData here
|
|
28
23
|
let isLoading = $state(true);
|
|
29
24
|
let error = $state<string | null>(null);
|
|
30
25
|
|
|
@@ -33,9 +28,12 @@
|
|
|
33
28
|
let modalTargetSchemaName = $state<string>('');
|
|
34
29
|
let modalTargetResourceName = $state<string>('');
|
|
35
30
|
let modalTargetResourceType = $state<ModalResourceType>('table');
|
|
36
|
-
let modalTargetColumns = $state<
|
|
31
|
+
let modalTargetColumns = $state<RLColumnMetadata[]>([]); // Use RLColumnMetadata
|
|
37
32
|
let modalTargetOperation = $state<APIOperation>('GET');
|
|
38
|
-
|
|
33
|
+
// For function parameters, the RLApiOperationModal might expect PrismFunctionParameter directly from prism-ts,
|
|
34
|
+
// or we transform them before passing. Let's assume it takes PrismFunctionParameter for now or RLFunctionParameter.
|
|
35
|
+
// The RLFunctionMetadata in RLSchemaData contains RLFunctionParameter.
|
|
36
|
+
let modalTargetFunctionParams = $state<PrismFunctionParameter[] | null>(null); // Or RLFunctionParameter[]
|
|
39
37
|
let modalInitialId = $state<string | number | null>(null);
|
|
40
38
|
let modalInitialDataForPut = $state<Record<string, any>>({});
|
|
41
39
|
|
|
@@ -43,35 +41,36 @@
|
|
|
43
41
|
let showEnumModal = $state(false);
|
|
44
42
|
let currentEnumDetails = $state<{ name: string; values: string[]; schema: string } | null>(null);
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
function computeCurrentSelectedSchema():
|
|
44
|
+
|
|
45
|
+
function computeCurrentSelectedSchema(): RLSchemaData | null { // Use RLSchemaData
|
|
48
46
|
const activeName = explorerStore.activeSchemaName;
|
|
49
|
-
const currentSchemas = schemas;
|
|
47
|
+
const currentSchemas = schemas;
|
|
50
48
|
if (!activeName || !currentSchemas) {
|
|
51
49
|
return null;
|
|
52
50
|
}
|
|
53
51
|
return currentSchemas.find(s => s.name === activeName) || null;
|
|
54
52
|
}
|
|
55
|
-
// Use the computation function with $derived
|
|
56
53
|
let currentSelectedSchemaObject = $derived(computeCurrentSelectedSchema());
|
|
57
54
|
|
|
58
|
-
// --- Computation function for currentEntityItems ---
|
|
59
55
|
function computeCurrentEntityItems(): Record<string, any> | null {
|
|
60
|
-
const schemaObj = currentSelectedSchemaObject;
|
|
56
|
+
const schemaObj = currentSelectedSchemaObject;
|
|
61
57
|
const activeType = explorerStore.activeEntityType;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
|
|
59
|
+
// Ensure explorerStore.activeEntityType is a valid key for RLSchemaData
|
|
60
|
+
if (schemaObj && activeType && schemaObj[activeType as keyof RLSchemaData]) {
|
|
61
|
+
const entities = schemaObj[activeType as keyof RLSchemaData]; // Type assertion
|
|
62
|
+
// Check if entities is not undefined and is an object
|
|
63
|
+
if (entities && typeof entities === 'object' && !Array.isArray(entities)) {
|
|
64
|
+
return (Object.keys(entities).length > 0) ? entities as Record<string, any> : null;
|
|
65
|
+
}
|
|
65
66
|
}
|
|
66
67
|
return null;
|
|
67
68
|
}
|
|
68
|
-
// Use the computation function with $derived
|
|
69
69
|
let currentEntityItems = $derived(computeCurrentEntityItems());
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
// --- Effects ---
|
|
73
72
|
$effect(() => {
|
|
74
|
-
//
|
|
73
|
+
// ... (scroll effect remains the same, uses derived values) ...
|
|
75
74
|
const schemaObj = currentSelectedSchemaObject;
|
|
76
75
|
const focusedName = explorerStore.focusedEntityName;
|
|
77
76
|
const activeType = explorerStore.activeEntityType;
|
|
@@ -100,63 +99,11 @@
|
|
|
100
99
|
try {
|
|
101
100
|
isLoading = true;
|
|
102
101
|
error = null;
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
const transformColumn = (apiCol: any): ColumnMetadata => ({
|
|
107
|
-
name: apiCol.name,
|
|
108
|
-
type: apiCol.type,
|
|
109
|
-
nullable: apiCol.nullable === true,
|
|
110
|
-
isPrimaryKey: apiCol.is_pk === true || apiCol.isPrimaryKey === true, // Handles both potential key names
|
|
111
|
-
isEnum: apiCol.is_enum === true,
|
|
112
|
-
references: apiCol.references ? {
|
|
113
|
-
schema: apiCol.references.schema_name || apiCol.references.schema,
|
|
114
|
-
table: apiCol.references.table,
|
|
115
|
-
column: apiCol.references.column,
|
|
116
|
-
} : undefined,
|
|
117
|
-
});
|
|
102
|
+
// Fetch raw schemas using prism-ts types
|
|
103
|
+
const rawFetchedSchemasFromPrism = await apiStore.prism.getSchemas() as PrismSchemaMetadata[];
|
|
118
104
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
schema: apiEntity.schema_name || apiEntity.schema || schemaName,
|
|
122
|
-
columns: (apiEntity.columns || []).map(transformColumn),
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const transformEnum = (apiEnum: any, schemaName: string): EnumMetadata => ({
|
|
126
|
-
name: apiEnum.name,
|
|
127
|
-
schema: apiEnum.schema_name || apiEnum.schema || schemaName,
|
|
128
|
-
values: apiEnum.values || [],
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const transformFnParam = (param: any): FunctionParameter => ({
|
|
132
|
-
name: param.name,
|
|
133
|
-
type: param.type,
|
|
134
|
-
mode: param.mode || 'IN',
|
|
135
|
-
hasDefault: param.has_default === true,
|
|
136
|
-
defaultValue: param.default_value === undefined ? null : String(param.default_value),
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const transformFunctionLike = (fn: any, schemaName: string): FunctionMetadata => ({
|
|
140
|
-
name: fn.name,
|
|
141
|
-
schema: fn.schema_name || fn.schema || schemaName,
|
|
142
|
-
type: String(fn.type),
|
|
143
|
-
objectType: String(fn.object_type),
|
|
144
|
-
description: fn.description === undefined ? null : fn.description,
|
|
145
|
-
parameters: (fn.parameters || []).map(transformFnParam),
|
|
146
|
-
returnType: fn.return_type === undefined ? null : fn.return_type,
|
|
147
|
-
isStrict: fn.is_strict === true,
|
|
148
|
-
...(fn.object_type && String(fn.object_type).includes('TRIGGER') && fn.trigger_data ? { triggerData: fn.trigger_data } : {})
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const transformedSchemasResult: SchemaMetadata[] = rawFetchedSchemas.map((apiSchema: any) => ({
|
|
152
|
-
name: apiSchema.name,
|
|
153
|
-
tables: Object.fromEntries(Object.entries(apiSchema.tables || {}).map(([k, v]) => [k, transformTableOrView(v, apiSchema.name) as TableMetadata])),
|
|
154
|
-
views: Object.fromEntries(Object.entries(apiSchema.views || {}).map(([k, v]) => [k, transformTableOrView(v, apiSchema.name) as ViewMetadata])),
|
|
155
|
-
enums: Object.fromEntries(Object.entries(apiSchema.enums || {}).map(([k, v]) => [k, transformEnum(v, apiSchema.name)])),
|
|
156
|
-
functions: Object.fromEntries(Object.entries(apiSchema.functions || {}).map(([k, v]) => [k, transformFunctionLike(v, apiSchema.name)])),
|
|
157
|
-
procedures: Object.fromEntries(Object.entries(apiSchema.procedures || {}).map(([k, v]) => [k, transformFunctionLike(v, apiSchema.name)])),
|
|
158
|
-
triggers: Object.fromEntries(Object.entries(apiSchema.triggers || {}).map(([k, v]) => [k, transformFunctionLike(v, apiSchema.name)])),
|
|
159
|
-
}));
|
|
105
|
+
// Use the new transformer
|
|
106
|
+
const transformedSchemasResult = transformPrismSchemasToRLData(rawFetchedSchemasFromPrism);
|
|
160
107
|
|
|
161
108
|
schemas = transformedSchemasResult;
|
|
162
109
|
|
|
@@ -177,26 +124,37 @@
|
|
|
177
124
|
loadAndTransformSchemas();
|
|
178
125
|
});
|
|
179
126
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return entities ? Object.keys(entities).length : 0;
|
|
127
|
+
function getEntityCount(schema: RLSchemaData | null, entityType: StoreExplorerEntityType): number {
|
|
128
|
+
if (!schema || !schema[entityType as keyof RLSchemaData]) return 0;
|
|
129
|
+
const entities = schema[entityType as keyof RLSchemaData];
|
|
130
|
+
return (entities && typeof entities === 'object' && !Array.isArray(entities)) ? Object.keys(entities).length : 0;
|
|
185
131
|
}
|
|
186
|
-
|
|
187
|
-
function
|
|
132
|
+
|
|
133
|
+
// This function should now expect RLTableMetadata or RLViewMetadata and return RLColumnMetadata[]
|
|
134
|
+
function getColumnsForTableOrView(item: RLTableMetadata | RLViewMetadata): RLColumnMetadata[] {
|
|
188
135
|
return item.columns || [];
|
|
189
136
|
}
|
|
190
137
|
|
|
191
|
-
|
|
192
|
-
function openModalForApi( // Renamed to openModalForApi
|
|
138
|
+
function openModalForApi(
|
|
193
139
|
params: { operation: APIOperation },
|
|
194
140
|
currentSchemaFromCall: string,
|
|
195
141
|
currentResourceName: string,
|
|
196
142
|
currentModalResourceType: ModalResourceType,
|
|
197
143
|
options: {
|
|
198
|
-
|
|
199
|
-
|
|
144
|
+
// Expect RLColumnMetadata for consistency if RLApiOperationModal is adapted
|
|
145
|
+
columns?: RLColumnMetadata[];
|
|
146
|
+
// For function parameters, `itemData as RLFunctionMetadata` will have `parameters` of type RLFunctionParameter[]
|
|
147
|
+
// The RLApiOperationModal props expect PrismFunctionParameter[] (or null).
|
|
148
|
+
// We need to either adapt RLApiOperationModal or transform back if strictly necessary.
|
|
149
|
+
// For simplicity, let's assume RLApiOperationModal can be adapted or we can transform `RLFunctionParameter[]`
|
|
150
|
+
// back to `PrismFunctionParameter[]` if RLApiOperationModal MUST take the prism-ts type.
|
|
151
|
+
// If RLApiOperationModal is changed to use RLFunctionParameter, then no conversion needed.
|
|
152
|
+
// If it stays with PrismFunctionParameter, then:
|
|
153
|
+
// functionParams?: PrismFunctionParameter[] | null;
|
|
154
|
+
// And when calling:
|
|
155
|
+
// functionParams: (itemData as RLFunctionMetadata).parameters.map(transformRLParamToPrismParam)
|
|
156
|
+
// Let's keep modalTargetFunctionParams as PrismFunctionParameter for now as it was
|
|
157
|
+
functionParams?: PrismFunctionParameter[] | null; // Sticking to what modal currently expects from props
|
|
200
158
|
initialId?: string | number | null;
|
|
201
159
|
initialDataForPut?: Record<string, any>;
|
|
202
160
|
} = {}
|
|
@@ -214,28 +172,73 @@
|
|
|
214
172
|
|
|
215
173
|
function closeModal() {
|
|
216
174
|
isModalOpen = false;
|
|
217
|
-
|
|
218
|
-
modalTargetResourceName = '';
|
|
219
|
-
modalTargetColumns = [];
|
|
220
|
-
modalTargetFunctionParams = null;
|
|
221
|
-
modalInitialId = null;
|
|
222
|
-
modalInitialDataForPut = {};
|
|
175
|
+
// ... (reset modal state properties) ...
|
|
223
176
|
}
|
|
224
177
|
|
|
225
|
-
|
|
226
|
-
function handleFkReferenceClicked(ref: ColumnReference) {
|
|
178
|
+
function handleFkReferenceClicked(ref: RLColumnReference) { // Expects RLColumnReference
|
|
227
179
|
explorerStore.navigateToEntity(ref.schema, 'tables', ref.table);
|
|
228
180
|
}
|
|
229
181
|
|
|
230
|
-
|
|
231
|
-
|
|
182
|
+
// RLEnumMetadata provides name, values, schema directly
|
|
183
|
+
function handleEnumBadgeClicked(enumData: RLEnumMetadata) {
|
|
184
|
+
currentEnumDetails = { name: enumData.name, values: enumData.values, schema: enumData.schema };
|
|
232
185
|
showEnumModal = true;
|
|
233
186
|
}
|
|
234
187
|
</script>
|
|
235
188
|
|
|
236
189
|
<!-- Template for src/lib/components/explorer/RLSchemaExplorer.svelte -->
|
|
237
|
-
<!--
|
|
190
|
+
<!-- ... (Template remains largely the same, but ensure data bindings use RL... types) ... -->
|
|
191
|
+
<!-- Example changes in template: -->
|
|
192
|
+
<!-- For RLMetadataTable:
|
|
193
|
+
columns={getColumnsForTableOrView(typedItem as (RLTableMetadata | RLViewMetadata))}
|
|
194
|
+
enumsInSchema={activeSchema.enums as Record<string, RLEnumMetadata>} // Cast might be needed
|
|
195
|
+
onEnumClick={(enumData) => handleEnumBadgeClicked(enumData)} // This will now pass RLEnumMetadata
|
|
196
|
+
-->
|
|
197
|
+
<!-- For RLApiInterface call:
|
|
198
|
+
columns={getColumnsForTableOrView(typedItem as (RLTableMetadata | RLViewMetadata))}
|
|
199
|
+
onOpenModal={(opParams) => openModalForApi(
|
|
200
|
+
opParams,
|
|
201
|
+
activeSchema.name,
|
|
202
|
+
name,
|
|
203
|
+
explorerStore.activeEntityType === 'tables' ? 'table' : 'view',
|
|
204
|
+
// Pass RLColumnMetadata
|
|
205
|
+
{ columns: getColumnsForTableOrView(typedItem as (RLTableMetadata | RLViewMetadata)) }
|
|
206
|
+
)}
|
|
207
|
+
-->
|
|
208
|
+
<!-- For Function/Procedure RLApiInterface call:
|
|
209
|
+
onOpenModal={(opParams) => {
|
|
210
|
+
const funcMeta = typedItem as RLFunctionMetadata;
|
|
211
|
+
// IMPORTANT: Here's where a decision for functionParams is needed.
|
|
212
|
+
// If RLApiOperationModal expects PrismFunctionParameter[]:
|
|
213
|
+
// You would need a function `transformRLParamToPrismParam` or map directly here.
|
|
214
|
+
// For now, assuming RLFunctionMetadata.parameters IS what RLApiOperationModal's functionParams prop wants (which is PrismFunctionParameter[]).
|
|
215
|
+
// This implies that our RLFunctionParameter and PrismFunctionParameter are structurally compatible or RLApiOperationModal is adapted.
|
|
216
|
+
// If RLFunctionParameter IS different from PrismFunctionParameter in a way that matters to the modal:
|
|
217
|
+
// const prismParams: PrismFunctionParameter[] = funcMeta.parameters.map(rlParam => ({...rlParam, defaultValue: rlParam.defaultValue ?? undefined }));
|
|
218
|
+
// The current RLFunctionParameter in types/explorer.ts matches PrismFunctionParameter structure sufficiently for the RLApiOperationModal to consume if `functionParams` prop remains PrismFunctionParameter[]
|
|
219
|
+
|
|
220
|
+
openModalForApi(
|
|
221
|
+
opParams,
|
|
222
|
+
activeSchema.name,
|
|
223
|
+
name,
|
|
224
|
+
'function',
|
|
225
|
+
// Assuming RLApiOperationModal expects PrismFunctionParameter[] and our RLFunctionParameter[] from typedItem is compatible.
|
|
226
|
+
// If types diverge significantly, a mapping function would be needed here.
|
|
227
|
+
// The `modalTargetFunctionParams = $state<PrismFunctionParameter[] | null>(null);` means we have to ensure the structure is compatible or map it.
|
|
228
|
+
// Our RLFunctionParameter looks very similar to PrismFunctionParameter in the example `RLApiOperationModal`.
|
|
229
|
+
{ functionParams: (typedItem as RLFunctionMetadata).parameters as unknown as PrismFunctionParameter[] } // Cast if shapes are compatible
|
|
230
|
+
);
|
|
231
|
+
}}
|
|
232
|
+
-->
|
|
233
|
+
<!-- In the #each Object.entries(currentEntityItems) -->
|
|
234
|
+
<!-- Change itemData casts: -->
|
|
235
|
+
<!-- {@const typedItem = itemData as (RLTableMetadata | RLViewMetadata)} -->
|
|
236
|
+
<!-- {@const typedItem = itemData as RLEnumMetadata} -->
|
|
237
|
+
<!-- {@const typedItem = itemData as RLFunctionMetadata} -->
|
|
238
238
|
|
|
239
|
+
<!-- RLApiOperationModal call needs to match prop types. If RLApiOperationModal's `columns` prop changes to RLColumnMetadata[], then no cast is needed.
|
|
240
|
+
Otherwise, `modalTargetColumns as unknown as PrismColumnMetadata[]` might be needed.
|
|
241
|
+
Similar for `functionParams`. -->
|
|
239
242
|
<div class="container mx-auto p-4">
|
|
240
243
|
{#if isLoading}
|
|
241
244
|
<div class="flex flex-col items-center justify-center h-64">
|
|
@@ -298,7 +301,7 @@
|
|
|
298
301
|
{@const entityId = `entity-${activeSchema.name}-${explorerStore.activeEntityType}-${name}`}
|
|
299
302
|
<!-- Display for Tables and Views -->
|
|
300
303
|
{#if explorerStore.activeEntityType === 'tables' || explorerStore.activeEntityType === 'views'}
|
|
301
|
-
{@const typedItem = itemData as (
|
|
304
|
+
{@const typedItem = itemData as (RLTableMetadata | RLViewMetadata)}
|
|
302
305
|
<div class="collapse collapse-arrow bg-base-100 shadow-md {explorerStore.focusedEntityName === name ? 'collapse-open ring-1 ring-primary ring-offset-base-100 ring-offset-2' : ''}" id={entityId}>
|
|
303
306
|
<input
|
|
304
307
|
type="checkbox"
|
|
@@ -317,16 +320,22 @@
|
|
|
317
320
|
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); explorerStore.focusOnEntity(explorerStore.focusedEntityName === name ? null : name);}}}
|
|
318
321
|
>
|
|
319
322
|
{name}
|
|
320
|
-
<span class="badge badge-ghost ml-2 capitalize">{explorerStore.activeEntityType
|
|
323
|
+
<span class="badge badge-ghost ml-2 capitalize">{explorerStore.activeEntityType === 'tables' ? 'Table' : 'View'}</span>
|
|
321
324
|
</div>
|
|
322
325
|
<div class="collapse-content">
|
|
323
326
|
<RLMetadataTable
|
|
324
327
|
title={name}
|
|
325
328
|
itemType={explorerStore.activeEntityType === 'tables' ? 'table' : 'view'}
|
|
326
329
|
columns={getColumnsForTableOrView(typedItem)}
|
|
327
|
-
enumsInSchema={activeSchema.enums}
|
|
330
|
+
enumsInSchema={activeSchema.enums as Record<string, RLEnumMetadata>}
|
|
328
331
|
onFkClick={handleFkReferenceClicked}
|
|
329
|
-
onEnumClick={(
|
|
332
|
+
onEnumClick={(enumDataFromTable) => {
|
|
333
|
+
// RLMetadataTable will pass the RLEnumMetadata for the clicked enum
|
|
334
|
+
// (or details if `onEnumClick` prop in RLMetadataTable is updated to send just `name`, `values`)
|
|
335
|
+
// Assuming it passes the full RLEnumMetadata or an object with {name, values} and schema is added here
|
|
336
|
+
const foundEnum = activeSchema.enums[enumDataFromTable.name]; // Or how enumDataFromTable is structured
|
|
337
|
+
if(foundEnum) handleEnumBadgeClicked(foundEnum);
|
|
338
|
+
}}
|
|
330
339
|
/>
|
|
331
340
|
<div class="mt-4 p-2 border-t border-base-300">
|
|
332
341
|
<RLApiInterface
|
|
@@ -347,7 +356,7 @@
|
|
|
347
356
|
</div>
|
|
348
357
|
<!-- Display for Enums -->
|
|
349
358
|
{:else if explorerStore.activeEntityType === 'enums'}
|
|
350
|
-
{@const typedItem = itemData as
|
|
359
|
+
{@const typedItem = itemData as RLEnumMetadata}
|
|
351
360
|
<div class="collapse collapse-arrow bg-base-100 shadow-md" id={entityId}>
|
|
352
361
|
<input type="checkbox" name="item-accordion-{activeSchema.name}-enum-{name}" />
|
|
353
362
|
<div
|
|
@@ -366,14 +375,14 @@
|
|
|
366
375
|
<li>{value}</li>
|
|
367
376
|
{/each}
|
|
368
377
|
</ul>
|
|
369
|
-
<button class="btn btn-xs btn-outline mt-2" onclick={() => handleEnumBadgeClicked(
|
|
378
|
+
<button class="btn btn-xs btn-outline mt-2" onclick={() => handleEnumBadgeClicked(typedItem)}>
|
|
370
379
|
Show Details
|
|
371
380
|
</button>
|
|
372
381
|
</div>
|
|
373
382
|
</div>
|
|
374
|
-
<!-- Display for Functions, Procedures, Triggers -->
|
|
383
|
+
<!-- Display for Functions, Procedures, Triggers (now consolidated under RLFunctionMetadata) -->
|
|
375
384
|
{:else if explorerStore.activeEntityType === 'functions' || explorerStore.activeEntityType === 'procedures' || explorerStore.activeEntityType === 'triggers'}
|
|
376
|
-
{@const typedItem = itemData as
|
|
385
|
+
{@const typedItem = itemData as RLFunctionMetadata}
|
|
377
386
|
<div class="collapse collapse-arrow bg-base-100 shadow-md" id={entityId}>
|
|
378
387
|
<input type="checkbox" name="item-accordion-{activeSchema.name}-{explorerStore.activeEntityType}-{name}" />
|
|
379
388
|
<div
|
|
@@ -383,7 +392,8 @@
|
|
|
383
392
|
onclick={(e) => { const input = e.currentTarget.previousElementSibling as HTMLInputElement; if(input) input.checked = !input.checked;}}
|
|
384
393
|
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); const input = e.currentTarget.previousElementSibling as HTMLInputElement; if(input) input.checked = !input.checked;}}}
|
|
385
394
|
>
|
|
386
|
-
|
|
395
|
+
<!-- Display 'kind' which now includes PROCEDURE, TRIGGER etc. -->
|
|
396
|
+
{name} <span class="badge badge-ghost ml-2 capitalize">{typedItem.kind.toLowerCase()}</span>
|
|
387
397
|
</div>
|
|
388
398
|
<div class="collapse-content">
|
|
389
399
|
{#if typedItem.description}
|
|
@@ -411,7 +421,7 @@
|
|
|
411
421
|
<p class="text-sm opacity-70 mt-2">No parameters.</p>
|
|
412
422
|
{/if}
|
|
413
423
|
|
|
414
|
-
{#if
|
|
424
|
+
{#if typedItem.kind === 'FUNCTION' || typedItem.kind === 'PROCEDURE'}
|
|
415
425
|
<div class="mt-4 p-2 border-t border-base-300">
|
|
416
426
|
<RLApiInterface
|
|
417
427
|
schemaName={activeSchema.name}
|
|
@@ -423,7 +433,9 @@
|
|
|
423
433
|
activeSchema.name,
|
|
424
434
|
name,
|
|
425
435
|
'function',
|
|
426
|
-
|
|
436
|
+
// typedItem.parameters are RLFunctionParameter[]. RLApiOperationModal expects PrismFunctionParameter[]
|
|
437
|
+
// Assuming direct castability for now due to similar structure
|
|
438
|
+
{ functionParams: typedItem.parameters as unknown as PrismFunctionParameter[] }
|
|
427
439
|
)}
|
|
428
440
|
/>
|
|
429
441
|
</div>
|
|
@@ -459,8 +471,8 @@
|
|
|
459
471
|
onClose={closeModal}
|
|
460
472
|
schemaName={modalTargetSchemaName}
|
|
461
473
|
resourceName={modalTargetResourceName}
|
|
462
|
-
operation={modalTargetOperation}
|
|
463
|
-
columns={modalTargetColumns}
|
|
474
|
+
operation={modalTargetOperation}
|
|
475
|
+
columns={modalTargetColumns as unknown as PrismColumnMetadata[]}
|
|
464
476
|
functionParams={modalTargetFunctionParams}
|
|
465
477
|
resourceType={modalTargetResourceType}
|
|
466
478
|
initialId={modalInitialId}
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
<!-- src/lib/components/forms/RLFilterForm.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
3
|
import type { ColumnMetadata } from '@yrrrrrf/prism-ts';
|
|
4
|
-
|
|
5
|
-
// Same helper as in RLResourceForm, can be moved to a shared utility file
|
|
6
|
-
function mapSqlTypeToInputType(sqlType: string): string {
|
|
7
|
-
const lowerSqlType = sqlType.toLowerCase();
|
|
8
|
-
if (lowerSqlType.includes('bool')) return 'checkbox'; // Or a select with true/false/any
|
|
9
|
-
if (lowerSqlType.includes('int') || lowerSqlType.includes('serial') || lowerSqlType.includes('numeric') || lowerSqlType.includes('decimal') || lowerSqlType.includes('real') || lowerSqlType.includes('double')) return 'number';
|
|
10
|
-
if (lowerSqlType.includes('date') && !lowerSqlType.includes('timestamp')) return 'date';
|
|
11
|
-
if (lowerSqlType.includes('timestamp')) return 'datetime-local';
|
|
12
|
-
if (lowerSqlType.includes('time') && !lowerSqlType.includes('timestamp')) return 'time';
|
|
13
|
-
// For filters, textarea is less common unless for full-text search
|
|
14
|
-
return 'text'; // Default for filters
|
|
15
|
-
}
|
|
16
|
-
|
|
4
|
+
import { mapSqlTypeToInputType } from '../../tools/form-helpers.js';
|
|
17
5
|
|
|
18
6
|
let {
|
|
19
7
|
columns,
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
<!-- src/lib/components/forms/RLFunctionForm.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
3
|
import type { FunctionParameter } from '@yrrrrrf/prism-ts';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
function mapSqlTypeToInputType(sqlType: string): string {
|
|
7
|
-
const lowerSqlType = sqlType.toLowerCase();
|
|
8
|
-
if (lowerSqlType.includes('bool')) return 'checkbox';
|
|
9
|
-
if (lowerSqlType.includes('int') || lowerSqlType.includes('serial') || lowerSqlType.includes('numeric')) return 'number';
|
|
10
|
-
if (lowerSqlType.includes('date') && !lowerSqlType.includes('timestamp')) return 'date';
|
|
11
|
-
if (lowerSqlType.includes('timestamp')) return 'datetime-local';
|
|
12
|
-
if (lowerSqlType.includes('time') && !lowerSqlType.includes('timestamp')) return 'time';
|
|
13
|
-
if (lowerSqlType.includes('text')) return 'textarea';
|
|
14
|
-
if (lowerSqlType.includes('json')) return 'textarea';
|
|
15
|
-
return 'text';
|
|
16
|
-
}
|
|
4
|
+
import { mapSqlTypeToInputType } from '../../tools/form-helpers.js';
|
|
5
|
+
|
|
17
6
|
|
|
18
7
|
let {
|
|
19
8
|
params, // FunctionParameter[]
|
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
<!-- src/lib/components/forms/RLResourceForm.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
3
|
import type { ColumnMetadata } from '@yrrrrrf/prism-ts';
|
|
4
|
+
import { mapSqlTypeToInputType } from '../../tools/form-helpers.js';
|
|
4
5
|
|
|
5
|
-
// Helper to map SQL types to HTML input types
|
|
6
|
-
function mapSqlTypeToInputType(sqlType: string, columnName: string): string {
|
|
7
|
-
const lowerSqlType = sqlType.toLowerCase();
|
|
8
|
-
if (lowerSqlType.includes('bool')) return 'checkbox';
|
|
9
|
-
if (lowerSqlType.includes('int') || lowerSqlType.includes('serial') || lowerSqlType.includes('numeric') || lowerSqlType.includes('decimal') || lowerSqlType.includes('real') || lowerSqlType.includes('double')) return 'number';
|
|
10
|
-
if (lowerSqlType.includes('date') && !lowerSqlType.includes('timestamp')) return 'date';
|
|
11
|
-
if (lowerSqlType.includes('timestamp')) return 'datetime-local';
|
|
12
|
-
if (lowerSqlType.includes('time') && !lowerSqlType.includes('timestamp')) return 'time';
|
|
13
|
-
if (lowerSqlType.includes('text') || columnName.includes('description')) return 'textarea'; // Heuristic for textarea
|
|
14
|
-
if (lowerSqlType.includes('json')) return 'textarea'; // JSON often edited as text
|
|
15
|
-
// Add more mappings as needed (e.g., for enums to select, bytea to file (later))
|
|
16
|
-
return 'text'; // Default
|
|
17
|
-
}
|
|
18
6
|
|
|
19
7
|
let {
|
|
20
8
|
columns,
|
|
@@ -23,4 +23,60 @@ declare class ExplorerStore {
|
|
|
23
23
|
reset(): void;
|
|
24
24
|
}
|
|
25
25
|
export declare const explorerStore: ExplorerStore;
|
|
26
|
+
export interface RLColumnReference {
|
|
27
|
+
schema: string;
|
|
28
|
+
table: string;
|
|
29
|
+
column: string;
|
|
30
|
+
}
|
|
31
|
+
export interface RLColumnMetadata {
|
|
32
|
+
name: string;
|
|
33
|
+
type: string;
|
|
34
|
+
nullable: boolean;
|
|
35
|
+
isPrimaryKey: boolean;
|
|
36
|
+
isEnum: boolean;
|
|
37
|
+
references?: RLColumnReference;
|
|
38
|
+
}
|
|
39
|
+
export interface RLBaseEntityMetadata {
|
|
40
|
+
name: string;
|
|
41
|
+
schema: string;
|
|
42
|
+
}
|
|
43
|
+
export interface RLTableMetadata extends RLBaseEntityMetadata {
|
|
44
|
+
columns: RLColumnMetadata[];
|
|
45
|
+
}
|
|
46
|
+
export interface RLViewMetadata extends RLBaseEntityMetadata {
|
|
47
|
+
columns: RLColumnMetadata[];
|
|
48
|
+
}
|
|
49
|
+
export interface RLEnumMetadata extends RLBaseEntityMetadata {
|
|
50
|
+
values: string[];
|
|
51
|
+
}
|
|
52
|
+
export interface RLFunctionParameter {
|
|
53
|
+
name: string;
|
|
54
|
+
type: string;
|
|
55
|
+
mode: "IN" | "OUT" | "INOUT" | "VARIADIC";
|
|
56
|
+
hasDefault: boolean;
|
|
57
|
+
defaultValue?: string | null;
|
|
58
|
+
}
|
|
59
|
+
export type RLFunctionKind = "SCALAR" | "TABLE" | "SET_RETURNING" | "AGGREGATE" | "WINDOW" | "PROCEDURE" | "TRIGGER" | "FUNCTION" | "UNKNOWN";
|
|
60
|
+
export interface RLFunctionMetadata extends RLBaseEntityMetadata {
|
|
61
|
+
kind: RLFunctionKind;
|
|
62
|
+
description?: string | null;
|
|
63
|
+
parameters: RLFunctionParameter[];
|
|
64
|
+
returnType?: string | null;
|
|
65
|
+
isStrict: boolean;
|
|
66
|
+
triggerData?: {
|
|
67
|
+
timing: string;
|
|
68
|
+
events: string[];
|
|
69
|
+
targetTableSchema: string;
|
|
70
|
+
targetTableName: string;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export interface RLSchemaData {
|
|
74
|
+
name: string;
|
|
75
|
+
tables: Record<string, RLTableMetadata>;
|
|
76
|
+
views: Record<string, RLViewMetadata>;
|
|
77
|
+
enums: Record<string, RLEnumMetadata>;
|
|
78
|
+
functions: Record<string, RLFunctionMetadata>;
|
|
79
|
+
procedures: Record<string, RLFunctionMetadata>;
|
|
80
|
+
triggers: Record<string, RLFunctionMetadata>;
|
|
81
|
+
}
|
|
26
82
|
export {};
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { apiStore } from "./components/stores/api.svelte.ts";
|
|
2
2
|
export { appData } from "./components/stores/app.svelte.ts";
|
|
3
|
+
export * from "./tools/schema-transformer.ts";
|
|
4
|
+
export * from "./tools/form-helpers.ts";
|
|
3
5
|
export declare function getVersion(): string;
|
|
4
6
|
export declare function init(): void;
|
package/dist/mod.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
export { apiStore } from "./components/stores/api.svelte.ts";
|
|
6
6
|
export { appData } from "./components/stores/app.svelte.ts";
|
|
7
7
|
// export { type AppData, appData } from "./stores/app.svelte.ts";
|
|
8
|
+
export * from "./tools/schema-transformer.ts";
|
|
9
|
+
export * from "./tools/form-helpers.ts";
|
|
8
10
|
// // * Import the main forge from the ts-forge library...
|
|
9
11
|
// export {
|
|
10
12
|
// type FooterConfig,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps SQL data types to HTML input element types.
|
|
3
|
+
* @param sqlType The SQL data type string.
|
|
4
|
+
* @param columnName (Optional) The name of the column, can be used for heuristics.
|
|
5
|
+
* @returns The corresponding HTML input type string.
|
|
6
|
+
*/
|
|
7
|
+
export declare function mapSqlTypeToInputType(sqlType: string, columnName?: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Determines the 'step' attribute for number inputs based on SQL type.
|
|
10
|
+
* @param sqlType The SQL data type string.
|
|
11
|
+
* @returns 'any' for floating point types, undefined otherwise.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getStepForNumberInput(sqlType: string): string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Formats a column reference object into a display string.
|
|
16
|
+
* @param ref The column reference object.
|
|
17
|
+
* @returns A string representation like "schema.table.column".
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatReferenceText(ref: {
|
|
20
|
+
schema: string;
|
|
21
|
+
table: string;
|
|
22
|
+
column: string;
|
|
23
|
+
} | undefined): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// File: src/lib/tools/form-helpers.ts
|
|
2
|
+
/**
|
|
3
|
+
* Maps SQL data types to HTML input element types.
|
|
4
|
+
* @param sqlType The SQL data type string.
|
|
5
|
+
* @param columnName (Optional) The name of the column, can be used for heuristics.
|
|
6
|
+
* @returns The corresponding HTML input type string.
|
|
7
|
+
*/
|
|
8
|
+
export function mapSqlTypeToInputType(sqlType, columnName) {
|
|
9
|
+
const lowerSqlType = sqlType.toLowerCase();
|
|
10
|
+
if (lowerSqlType.includes("bool"))
|
|
11
|
+
return "checkbox";
|
|
12
|
+
if (lowerSqlType.includes("int") || lowerSqlType.includes("serial") ||
|
|
13
|
+
lowerSqlType.includes("numeric") || lowerSqlType.includes("decimal") ||
|
|
14
|
+
lowerSqlType.includes("real") || lowerSqlType.includes("double"))
|
|
15
|
+
return "number";
|
|
16
|
+
if (lowerSqlType.includes("date") && !lowerSqlType.includes("timestamp"))
|
|
17
|
+
return "date";
|
|
18
|
+
if (lowerSqlType.includes("timestamp"))
|
|
19
|
+
return "datetime-local";
|
|
20
|
+
if (lowerSqlType.includes("time") && !lowerSqlType.includes("timestamp"))
|
|
21
|
+
return "time";
|
|
22
|
+
if (lowerSqlType.includes("text") ||
|
|
23
|
+
(columnName && columnName.toLowerCase().includes("description")))
|
|
24
|
+
return "textarea";
|
|
25
|
+
if (lowerSqlType.includes("json"))
|
|
26
|
+
return "textarea"; // JSON often best edited as text in simple forms
|
|
27
|
+
return "text"; // Default
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Determines the 'step' attribute for number inputs based on SQL type.
|
|
31
|
+
* @param sqlType The SQL data type string.
|
|
32
|
+
* @returns 'any' for floating point types, undefined otherwise.
|
|
33
|
+
*/
|
|
34
|
+
export function getStepForNumberInput(sqlType) {
|
|
35
|
+
const lowerSqlType = sqlType.toLowerCase();
|
|
36
|
+
if (lowerSqlType.includes("decimal") || lowerSqlType.includes("numeric") ||
|
|
37
|
+
lowerSqlType.includes("real") || lowerSqlType.includes("double")) {
|
|
38
|
+
return "any";
|
|
39
|
+
}
|
|
40
|
+
return undefined; // Default step (usually 1 for integers)
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Formats a column reference object into a display string.
|
|
44
|
+
* @param ref The column reference object.
|
|
45
|
+
* @returns A string representation like "schema.table.column".
|
|
46
|
+
*/
|
|
47
|
+
export function formatReferenceText(ref) {
|
|
48
|
+
if (!ref)
|
|
49
|
+
return "";
|
|
50
|
+
return `${ref.schema}.${ref.table}.${ref.column}`;
|
|
51
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ColumnMetadata as PrismColumnMetadata, FunctionMetadata as PrismFunctionMetadata, SchemaMetadata as PrismSchemaMetadata } from "@yrrrrrf/prism-ts";
|
|
2
|
+
import type { RLColumnMetadata, RLFunctionMetadata, RLSchemaData } from "../components/stores/explorer.svelte.ts";
|
|
3
|
+
export declare function transformPrismColumn(prismCol: PrismColumnMetadata): RLColumnMetadata;
|
|
4
|
+
export declare function transformPrismFunction(prismFn: PrismFunctionMetadata, schema: string): RLFunctionMetadata;
|
|
5
|
+
export declare function transformPrismSchemasToRLData(prismSchemas: PrismSchemaMetadata[]): RLSchemaData[];
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// File: src/lib/tools/schema-transformer.ts
|
|
2
|
+
// --- Helper Functions ---
|
|
3
|
+
function transformPrismColumnReference(ref) {
|
|
4
|
+
if (!ref)
|
|
5
|
+
return undefined;
|
|
6
|
+
return {
|
|
7
|
+
schema: ref.schema, // prism-ts seems to use 'schema' directly
|
|
8
|
+
table: ref.table,
|
|
9
|
+
column: ref.column,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function transformPrismColumn(prismCol) {
|
|
13
|
+
return {
|
|
14
|
+
name: prismCol.name,
|
|
15
|
+
type: prismCol.type,
|
|
16
|
+
nullable: prismCol.nullable,
|
|
17
|
+
// Adapting from prism-ts field `isPrimaryKey` to `isPrimaryKey`
|
|
18
|
+
// And `isEnum` to `isEnum`
|
|
19
|
+
isPrimaryKey: prismCol.isPrimaryKey === true,
|
|
20
|
+
isEnum: prismCol.isEnum === true,
|
|
21
|
+
references: transformPrismColumnReference(prismCol.references),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function transformPrismTableOrView(prismEntity, schema) {
|
|
25
|
+
return {
|
|
26
|
+
name: prismEntity.name,
|
|
27
|
+
schema: schema, // prism-ts's TableMetadata includes schema
|
|
28
|
+
columns: (prismEntity.columns || []).map(transformPrismColumn),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function transformPrismEnum(prismEnum, schema) {
|
|
32
|
+
return {
|
|
33
|
+
name: prismEnum.name,
|
|
34
|
+
schema: schema, // prism-ts's EnumMetadata includes schema
|
|
35
|
+
values: prismEnum.values || [],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function transformPrismFunctionParameter(param) {
|
|
39
|
+
return {
|
|
40
|
+
name: param.name,
|
|
41
|
+
type: param.type,
|
|
42
|
+
mode: param.mode || "IN", // prism-ts uses string
|
|
43
|
+
hasDefault: param.hasDefault === true,
|
|
44
|
+
defaultValue: param.defaultValue === undefined ? null : String(param.defaultValue),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function determineRLFunctionKind(prismFn) {
|
|
48
|
+
const objectType = (prismFn.objectType || "").toUpperCase();
|
|
49
|
+
const fnType = (prismFn.type || "").toUpperCase(); // e.g., SCALAR, TABLE, SET
|
|
50
|
+
if (objectType === "PROCEDURE")
|
|
51
|
+
return "PROCEDURE";
|
|
52
|
+
if (objectType === "TRIGGER")
|
|
53
|
+
return "TRIGGER";
|
|
54
|
+
if (objectType === "FUNCTION" || objectType === "") { // Default to FUNCTION if objectType is missing
|
|
55
|
+
if (fnType === "SCALAR")
|
|
56
|
+
return "SCALAR";
|
|
57
|
+
if (fnType === "TABLE")
|
|
58
|
+
return "TABLE";
|
|
59
|
+
if (fnType === "SET")
|
|
60
|
+
return "SET_RETURNING"; // prism-ts calls it 'set'
|
|
61
|
+
if (fnType === "AGGREGATE")
|
|
62
|
+
return "AGGREGATE";
|
|
63
|
+
if (fnType === "WINDOW")
|
|
64
|
+
return "WINDOW";
|
|
65
|
+
// todo: Check if prism-ts has other specific function types
|
|
66
|
+
// todo: If prism-ts has a 'FUNCTION' type, we can return it here
|
|
67
|
+
// todo: Check if we need to handle 'FUNCTION' as a generic type...
|
|
68
|
+
return "FUNCTION"; // Generic function if type is not more specific
|
|
69
|
+
// todo: (probably not needed, as we can use 'FUNCTION' for generic functions)
|
|
70
|
+
// todo: (probably not needed, as we can use 'FUNCTION' for generic functions)
|
|
71
|
+
// todo: (probably not needed, as we can use 'FUNCTION' for generic functions)
|
|
72
|
+
}
|
|
73
|
+
return "UNKNOWN";
|
|
74
|
+
}
|
|
75
|
+
export function transformPrismFunction(prismFn, schema) {
|
|
76
|
+
const kind = determineRLFunctionKind(prismFn);
|
|
77
|
+
const transformed = {
|
|
78
|
+
name: prismFn.name,
|
|
79
|
+
schema: schema, // prism-ts's FunctionMetadata includes schema
|
|
80
|
+
kind: kind,
|
|
81
|
+
description: prismFn.description === undefined ? null : prismFn.description,
|
|
82
|
+
parameters: (prismFn.parameters || []).map(transformPrismFunctionParameter),
|
|
83
|
+
returnType: prismFn.returnType === undefined ? null : prismFn.returnType,
|
|
84
|
+
isStrict: prismFn.isStrict === true,
|
|
85
|
+
};
|
|
86
|
+
// Example: If prism-ts provides trigger-specific data on its FunctionMetadata when objectType is 'trigger'
|
|
87
|
+
// We would map it here to transformed.triggerData
|
|
88
|
+
// if (kind === 'TRIGGER' && (prismFn as any).triggerData) {
|
|
89
|
+
// transformed.triggerData = {
|
|
90
|
+
// timing: (prismFn as any).triggerData.timing,
|
|
91
|
+
// events: (prismFn as any).triggerData.events,
|
|
92
|
+
// targetTableSchema: (prismFn as any).triggerData.table_schema,
|
|
93
|
+
// targetTableName: (prismFn as any).triggerData.table_name,
|
|
94
|
+
// };
|
|
95
|
+
// }
|
|
96
|
+
return transformed;
|
|
97
|
+
}
|
|
98
|
+
// --- Main Transformation Function ---
|
|
99
|
+
export function transformPrismSchemasToRLData(prismSchemas) {
|
|
100
|
+
return prismSchemas.map((prismSchema) => {
|
|
101
|
+
const rlSchema = {
|
|
102
|
+
name: prismSchema.name,
|
|
103
|
+
tables: {},
|
|
104
|
+
views: {},
|
|
105
|
+
enums: {},
|
|
106
|
+
functions: {},
|
|
107
|
+
procedures: {},
|
|
108
|
+
triggers: {},
|
|
109
|
+
};
|
|
110
|
+
Object.entries(prismSchema.tables || {}).forEach(([name, table]) => {
|
|
111
|
+
rlSchema.tables[name] = transformPrismTableOrView(table, prismSchema.name);
|
|
112
|
+
});
|
|
113
|
+
Object.entries(prismSchema.views || {}).forEach(([name, view]) => {
|
|
114
|
+
rlSchema.views[name] = transformPrismTableOrView(view, prismSchema.name);
|
|
115
|
+
});
|
|
116
|
+
Object.entries(prismSchema.enums || {}).forEach(([name, enumData]) => {
|
|
117
|
+
rlSchema.enums[name] = transformPrismEnum(enumData, prismSchema.name);
|
|
118
|
+
});
|
|
119
|
+
Object.entries(prismSchema.functions || {}).forEach(([name, func]) => {
|
|
120
|
+
rlSchema.functions[name] = transformPrismFunction(func, prismSchema.name);
|
|
121
|
+
});
|
|
122
|
+
Object.entries(prismSchema.procedures || {}).forEach(([name, proc]) => {
|
|
123
|
+
rlSchema.procedures[name] = transformPrismFunction(proc, prismSchema.name);
|
|
124
|
+
});
|
|
125
|
+
Object.entries(prismSchema.triggers || {}).forEach(([name, trig]) => {
|
|
126
|
+
rlSchema.triggers[name] = transformPrismFunction(trig, prismSchema.name);
|
|
127
|
+
});
|
|
128
|
+
return rlSchema;
|
|
129
|
+
});
|
|
130
|
+
}
|