rune-lab 0.0.8 → 0.0.9

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.
@@ -1,46 +1,52 @@
1
1
  <!-- src/lib/components/api/RLApiInterface.svelte -->
2
2
  <script lang="ts">
3
- import type { ColumnMetadata } from '@yrrrrrf/prism-ts';
4
- type APIOperation = 'GET' | 'POST' | 'PUT' | 'DELETE';
3
+ import type { ColumnMetadata as PrismColumnMetadata } from '@yrrrrrf/prism-ts';
4
+ // Import RLApiInterfaceActionParams from YOUR definition in the explorer store
5
+ import type { RLApiInterfaceActionParams } from '../stores/explorer.svelte.ts';
6
+
7
+ // For convenience within this component, you can extract the operation type.
8
+ // This is NOT redefining it; it's creating a local alias to a part of an imported type.
9
+ type LocalAPIOperation = RLApiInterfaceActionParams['operation'];
5
10
 
6
11
  let {
7
12
  schemaName,
8
13
  resourceName,
9
14
  resourceType,
10
15
  columns,
11
- onOpenModal // <-- ADDED THIS PROP
16
+ onOpenModal
12
17
  } = $props<{
13
18
  schemaName: string;
14
19
  resourceName: string;
15
- resourceType: 'table' | 'view' | 'function'; // Added 'function' for future
16
- columns: ColumnMetadata[]; // Still relevant for tables/views context
17
- onOpenModal: (params: { operation: APIOperation }) => void; // <-- TYPE FOR THE PROP
20
+ resourceType: 'table' | 'view' | 'function';
21
+ columns: PrismColumnMetadata[]; // This prop is fine if it's what the component receives
22
+ onOpenModal: (params: RLApiInterfaceActionParams) => void; // Uses your imported interface
18
23
  }>();
19
24
 
20
- function getAllowedOps(type: 'table' | 'view' | 'function'): APIOperation[] {
25
+ // Use the local alias or RLApiInterfaceActionParams['operation']
26
+ function getAllowedOps(type: 'table' | 'view' | 'function'): LocalAPIOperation[] {
21
27
  if (type === 'table') return ['GET', 'POST', 'PUT', 'DELETE'];
22
28
  if (type === 'view') return ['GET'];
23
- if (type === 'function') return ['POST']; // Typical for functions
29
+ if (type === 'function') return ['POST'];
24
30
  return [];
25
31
  }
26
32
 
27
- const operationDetails: Record<APIOperation, { label: string, class: string }> = {
33
+ const operationDetails: Record<LocalAPIOperation, { label: string, class: string }> = {
28
34
  GET: { label: 'GET', class: 'btn-info' },
29
35
  POST: { label: 'POST', class: 'btn-success' },
30
36
  PUT: { label: 'PUT', class: 'btn-warning' },
31
37
  DELETE: { label: 'DELETE', class: 'btn-error' },
32
38
  };
33
39
 
34
- function handleOperationClick(operation: APIOperation) {
40
+ function handleOperationClick(operation: LocalAPIOperation) {
41
+ // Construct the object that matches RLApiInterfaceActionParams
35
42
  onOpenModal({ operation });
36
43
  }
37
-
38
44
  </script>
39
45
 
40
46
  <div class="flex flex-wrap gap-2 my-2">
41
47
  {#each getAllowedOps(resourceType) as operation}
42
48
  {@const detail = operationDetails[operation]}
43
- {#if detail} <!-- Added a check in case an operation is not in details -->
49
+ {#if detail}
44
50
  <button
45
51
  class="btn btn-sm {detail.class}"
46
52
  onclick={() => handleOperationClick(operation)}
@@ -1,13 +1,11 @@
1
- import type { ColumnMetadata } from '@yrrrrrf/prism-ts';
2
- type APIOperation = 'GET' | 'POST' | 'PUT' | 'DELETE';
1
+ import type { ColumnMetadata as PrismColumnMetadata } from '@yrrrrrf/prism-ts';
2
+ import type { RLApiInterfaceActionParams } from '../stores/explorer.svelte.ts';
3
3
  type $$ComponentProps = {
4
4
  schemaName: string;
5
5
  resourceName: string;
6
6
  resourceType: 'table' | 'view' | 'function';
7
- columns: ColumnMetadata[];
8
- onOpenModal: (params: {
9
- operation: APIOperation;
10
- }) => void;
7
+ columns: PrismColumnMetadata[];
8
+ onOpenModal: (params: RLApiInterfaceActionParams) => void;
11
9
  };
12
10
  declare const RlApiInterface: import("svelte").Component<$$ComponentProps, {}, "">;
13
11
  type RlApiInterface = ReturnType<typeof RlApiInterface>;
@@ -0,0 +1,107 @@
1
+ <!-- src/lib/components/common/RLPropertyTable.svelte (example structure) -->
2
+ <script lang="ts">
3
+ import type { RLEnumMetadata, RLColumnReference } from '../stores/explorer.svelte';
4
+
5
+ export interface RLPropertyItem {
6
+ name: string;
7
+ type?: string;
8
+ nullable?: boolean;
9
+ isPrimaryKey?: boolean;
10
+ references?: RLColumnReference;
11
+ isEnum?: boolean;
12
+ mode?: string; // For function params
13
+ hasDefault?: boolean;
14
+ defaultValue?: string | null;
15
+ // Any other properties needed to render details
16
+ _rawItem?: any; // Optional: pass the original item if needed by slot
17
+ }
18
+
19
+ let {
20
+ items,
21
+ title = '',
22
+ nameHeader = "Name",
23
+ detailHeader = "Details",
24
+ enumsInSchema, // Pass this if needed for enum badge clicks from here
25
+ onFkClick,
26
+ onEnumClick,
27
+ } = $props<{
28
+ items: RLPropertyItem[];
29
+ title?: string;
30
+ nameHeader?: string;
31
+ detailHeader?: string;
32
+ enumsInSchema?: Record<string, RLEnumMetadata>; // For resolving enum names if items only have partial enum info
33
+ onFkClick?: (ref: RLColumnReference) => void;
34
+ onEnumClick?: (enumData: RLEnumMetadata) => void;
35
+ }>();
36
+ </script>
37
+
38
+ <div class="card bg-base-100 shadow-sm overflow-hidden my-2">
39
+ {#if title}
40
+ <div class="card-title p-3 text-sm font-semibold bg-base-200/50">{title}</div>
41
+ {/if}
42
+ <div class="card-body p-0">
43
+ {#if items && items.length > 0}
44
+ <div class="overflow-x-auto">
45
+ <table class="table table-sm w-full">
46
+ <thead>
47
+ <tr>
48
+ <th class="w-2/5 pl-4">{nameHeader}</th>
49
+ {#if detailHeader} <th class="w-3/5 pr-4">{detailHeader}</th> {/if}
50
+ </tr>
51
+ </thead>
52
+ <tbody>
53
+ {#each items as item (item.name)}
54
+ <tr class="hover">
55
+ <td class="align-top py-2 pl-4">
56
+ <div class="flex items-baseline">
57
+ <span class="font-medium">{item.name}</span>
58
+ {#if item.nullable === false && item.type && !item.type.toLowerCase().includes('bool')} <!-- Nullability for non-booleans -->
59
+ <span class="text-error ml-1 select-none" title="Required field">*</span>
60
+ {/if}
61
+ </div>
62
+ {#if item.type && detailHeader} <!-- Only show type if details are shown and type exists -->
63
+ <div class="font-mono text-xs text-base-content/60 italic mt-0.5">{item.type}</div>
64
+ {/if}
65
+ </td>
66
+ {#if detailHeader}
67
+ <td class="align-top py-2 pr-4 space-x-1.5">
68
+ {#if $$slots.detail}
69
+ <slot name="detail" itemData={item}></slot>
70
+ {:else}
71
+ <!-- Default detail rendering -->
72
+ {#if item.isPrimaryKey} <span class="badge badge-accent badge-xs font-semibold">PK</span> {/if}
73
+ {#if item.references}
74
+ <span class="badge badge-secondary badge-xs">FK</span>
75
+ <button class="link link-hover text-xs font-mono !text-info normal-case"
76
+ onclick={() => onFkClick && onFkClick(item.references!)}
77
+ title="Navigate to {item.references.schema}.{item.references.table}.{item.references.column}">
78
+ {item.references.schema}.{item.references.table}.{item.references.column}
79
+ </button>
80
+ {/if}
81
+ {#if item.isEnum && enumsInSchema && enumsInSchema[item.name + '_enum'] /* Crude example of finding enum */}
82
+ {@const enumMeta = enumsInSchema[item.name + '_enum']}
83
+ <button class="badge badge-warning badge-xs hover:shadow-md transition-shadow normal-case"
84
+ onclick={() => onEnumClick && onEnumClick(enumMeta)}
85
+ title="Enum: {enumMeta.name}">
86
+ <span class="mr-1 opacity-70">Enum:</span>{enumMeta.name}
87
+ </button>
88
+ {:else if item.isEnum}
89
+ <span class="badge badge-ghost badge-xs">Enum</span>
90
+ {/if}
91
+ {#if item.mode} <span class="badge badge-info badge-xs">{item.mode}</span> {/if}
92
+ {#if item.hasDefault} <span class="badge badge-outline badge-xs" title="Default: {item.defaultValue}">Has Default</span> {/if}
93
+ {/if}
94
+ </td>
95
+ {/if}
96
+ </tr>
97
+ {/each}
98
+ </tbody>
99
+ </table>
100
+ </div>
101
+ {:else}
102
+ <div class="p-4 text-center text-neutral-content/70">
103
+ No items to display.
104
+ </div>
105
+ {/if}
106
+ </div>
107
+ </div>
@@ -0,0 +1,44 @@
1
+ import type { RLEnumMetadata, RLColumnReference } from '../stores/explorer.svelte';
2
+ export interface RLPropertyItem {
3
+ name: string;
4
+ type?: string;
5
+ nullable?: boolean;
6
+ isPrimaryKey?: boolean;
7
+ references?: RLColumnReference;
8
+ isEnum?: boolean;
9
+ mode?: string;
10
+ hasDefault?: boolean;
11
+ defaultValue?: string | null;
12
+ _rawItem?: any;
13
+ }
14
+ type $$ComponentProps = {
15
+ items: RLPropertyItem[];
16
+ title?: string;
17
+ nameHeader?: string;
18
+ detailHeader?: string;
19
+ enumsInSchema?: Record<string, RLEnumMetadata>;
20
+ onFkClick?: (ref: RLColumnReference) => void;
21
+ onEnumClick?: (enumData: RLEnumMetadata) => void;
22
+ };
23
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
24
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
25
+ $$bindings?: Bindings;
26
+ } & Exports;
27
+ (internal: unknown, props: Props & {
28
+ $$events?: Events;
29
+ $$slots?: Slots;
30
+ }): Exports & {
31
+ $set?: any;
32
+ $on?: any;
33
+ };
34
+ z_$$bindings?: Bindings;
35
+ }
36
+ declare const RlPripertyTable: $$__sveltets_2_IsomorphicComponent<$$ComponentProps, {
37
+ [evt: string]: CustomEvent<any>;
38
+ }, {
39
+ detail: {
40
+ itemData: unknown;
41
+ };
42
+ }, {}, "">;
43
+ type RlPripertyTable = InstanceType<typeof RlPripertyTable>;
44
+ export default RlPripertyTable;
@@ -0,0 +1,61 @@
1
+ <!-- src/lib/components/dataview/RLDetailRow.svelte -->
2
+ <script lang="ts">
3
+ // No specific types needed from explorer.ts for these direct props.
4
+ // This component is highly generic.
5
+
6
+ type DetailAction = {
7
+ text: string;
8
+ class?: string;
9
+ onClick?: (event: MouseEvent) => void;
10
+ title?: string; // For tooltip on the badge/button
11
+ isLink?: boolean; // To render as a link-styled button
12
+ };
13
+
14
+ let {
15
+ name,
16
+ typeInfo = undefined,
17
+ details = [],
18
+ required = false,
19
+ description = undefined,
20
+ nameClass = 'font-medium',
21
+ typeInfoClass = 'font-mono text-xs text-base-content/60 italic mt-0.5',
22
+ containerClass = 'flex justify-between items-baseline py-1.5 hover:bg-base-content/5 px-1 rounded group',
23
+ } = $props<{
24
+ name: string;
25
+ typeInfo?: string;
26
+ details?: DetailAction[];
27
+ required?: boolean;
28
+ description?: string;
29
+ nameClass?: string;
30
+ typeInfoClass?: string;
31
+ containerClass?: string;
32
+ }>();
33
+ </script>
34
+
35
+ <div class="{containerClass}" title={description}>
36
+ <div class="flex-grow overflow-hidden pr-2">
37
+ <span class="{nameClass}">{name}</span>
38
+ {#if required}
39
+ <span class="text-error ml-1 select-none" title="Required field">*</span>
40
+ {/if}
41
+ {#if typeInfo}
42
+ <div class="{typeInfoClass} truncate" title={typeInfo}>{typeInfo}</div>
43
+ {/if}
44
+ </div>
45
+ {#if details.length > 0}
46
+ <div class="flex-shrink-0 space-x-1.5 flex items-center">
47
+ {#each details as detailItem}
48
+ <button
49
+ class="badge badge-sm whitespace-nowrap {detailItem.class || 'badge-ghost'}
50
+ {detailItem.isLink ? 'link link-hover !text-info normal-case font-mono' : ''}
51
+ disabled:opacity-50"
52
+ onclick={detailItem.onClick}
53
+ title={detailItem.title || detailItem.text}
54
+ disabled={!detailItem.onClick && !detailItem.isLink}
55
+ >
56
+ {@html detailItem.text} <!-- Use @html if text might contain simple entities like → -->
57
+ </button>
58
+ {/each}
59
+ </div>
60
+ {/if}
61
+ </div>
@@ -0,0 +1,20 @@
1
+ type DetailAction = {
2
+ text: string;
3
+ class?: string;
4
+ onClick?: (event: MouseEvent) => void;
5
+ title?: string;
6
+ isLink?: boolean;
7
+ };
8
+ type $$ComponentProps = {
9
+ name: string;
10
+ typeInfo?: string;
11
+ details?: DetailAction[];
12
+ required?: boolean;
13
+ description?: string;
14
+ nameClass?: string;
15
+ typeInfoClass?: string;
16
+ containerClass?: string;
17
+ };
18
+ declare const RlDetailRow: import("svelte").Component<$$ComponentProps, {}, "">;
19
+ type RlDetailRow = ReturnType<typeof RlDetailRow>;
20
+ export default RlDetailRow;
@@ -0,0 +1,53 @@
1
+ <!-- src/lib/components/dataview/RLMetadataList.svelte -->
2
+ <script lang="ts">
3
+ import RLDetailRow from './RLDetailRow.svelte';
4
+
5
+ type DetailActionForList = { // Prop structure for items passed to RLDetailRow
6
+ text: string;
7
+ class?: string;
8
+ onClick?: (event: MouseEvent) => void;
9
+ title?: string;
10
+ isLink?: boolean;
11
+ };
12
+
13
+ export interface RLMetadataListItem {
14
+ name: string;
15
+ typeInfo?: string;
16
+ details?: DetailActionForList[];
17
+ required?: boolean;
18
+ description?: string;
19
+ }
20
+
21
+ let {
22
+ title = undefined,
23
+ items = [],
24
+ listClass = 'divide-y divide-base-300/50',
25
+ emptyText = 'None.',
26
+ } = $props<{
27
+ title?: string;
28
+ items: RLMetadataListItem[];
29
+ listClass?: string;
30
+ emptyText?: string;
31
+ }>();
32
+ </script>
33
+
34
+ <div>
35
+ {#if title}
36
+ <h4 class="font-semibold text-sm mb-1 mt-2 opacity-80">{title}</h4>
37
+ {/if}
38
+ {#if items.length > 0}
39
+ <div class="{listClass} border-t border-b border-base-300/30 -mx-1">
40
+ {#each items as item (item.name) }
41
+ <RLDetailRow
42
+ name={item.name}
43
+ typeInfo={item.typeInfo}
44
+ details={item.details}
45
+ required={item.required}
46
+ description={item.description}
47
+ />
48
+ {/each}
49
+ </div>
50
+ {:else}
51
+ <p class="text-xs text-base-content/50 italic py-1.5 px-1">{emptyText}</p>
52
+ {/if}
53
+ </div>
@@ -0,0 +1,23 @@
1
+ type DetailActionForList = {
2
+ text: string;
3
+ class?: string;
4
+ onClick?: (event: MouseEvent) => void;
5
+ title?: string;
6
+ isLink?: boolean;
7
+ };
8
+ export interface RLMetadataListItem {
9
+ name: string;
10
+ typeInfo?: string;
11
+ details?: DetailActionForList[];
12
+ required?: boolean;
13
+ description?: string;
14
+ }
15
+ type $$ComponentProps = {
16
+ title?: string;
17
+ items: RLMetadataListItem[];
18
+ listClass?: string;
19
+ emptyText?: string;
20
+ };
21
+ declare const RlMetadataList: import("svelte").Component<$$ComponentProps, {}, "">;
22
+ type RlMetadataList = ReturnType<typeof RlMetadataList>;
23
+ export default RlMetadataList;
@@ -0,0 +1,34 @@
1
+ <!-- src/lib/components/explorer/RLEnumDisplay.svelte -->
2
+ <script lang="ts">
3
+ import type { RLEnumMetadata } from '../stores/explorer.svelte';
4
+ import RLMetadataList from '../dataview/RLMetadataList.svelte'; // Adjust path as needed
5
+
6
+ let {
7
+ enumData,
8
+ // Optional: if RLEnumDisplay should have its own modal button.
9
+ // Otherwise, RLSchemaExplorer can handle showing the modal if needed via context.
10
+ // For simplicity, let's assume any modal action is handled by the parent (RLSchemaExplorer).
11
+ } = $props<{
12
+ enumData: RLEnumMetadata;
13
+ }>();
14
+
15
+ let enumValueItems = $derived(
16
+ enumData.values.map((value: string) => ({
17
+ name: value,
18
+ }))
19
+ );
20
+ </script>
21
+
22
+ <div class="py-2">
23
+ <RLMetadataList items={enumValueItems} title="Values" emptyText="No values defined for this enum." />
24
+ <!--
25
+ If RLSchemaExplorer needs a way to open its existing enum modal from here,
26
+ you could add a button and an on:click event that bubbles up or calls a prop function.
27
+ Example:
28
+ <div class="mt-2">
29
+ <button class="btn btn-xs btn-outline" on:click>
30
+ Show in Modal (if needed)
31
+ </button>
32
+ </div>
33
+ -->
34
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { RLEnumMetadata } from '../stores/explorer.svelte';
2
+ type $$ComponentProps = {
3
+ enumData: RLEnumMetadata;
4
+ };
5
+ declare const RlEnumDisplay: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type RlEnumDisplay = ReturnType<typeof RlEnumDisplay>;
7
+ export default RlEnumDisplay;
@@ -0,0 +1,64 @@
1
+ <!-- src/lib/components/explorer/RLFunctionDisplay.svelte -->
2
+ <script lang="ts">
3
+ import type { RLApiInterfaceActionParams, RLFunctionMetadata, RLFunctionParameter } from '../stores/explorer.svelte';
4
+
5
+ import RLDetailRow from '../dataview/RLDetailRow.svelte';
6
+ import RLMetadataList from '../dataview/RLMetadataList.svelte';
7
+ import type { RLMetadataListItem } from '../dataview/RLMetadataList.svelte';
8
+ import RLApiInterface from '../api/RLApiInterface.svelte'; // For execution
9
+
10
+ let {
11
+ func,
12
+ onOpenApiModal, // Callback to RLSchemaExplorer
13
+ } = $props<{
14
+ func: RLFunctionMetadata;
15
+ onOpenApiModal: (params: RLApiInterfaceActionParams & { schemaName: string, resourceName: string, resourceType: 'function', options: any }) => void;
16
+ }>();
17
+
18
+ let parameterItems = $derived(
19
+ func.parameters.map((param: RLFunctionParameter): RLMetadataListItem => ({
20
+ name: param.name,
21
+ typeInfo: param.type + (param.defaultValue ? ` (default: ${param.defaultValue})` : ''),
22
+ required: !param.hasDefault && param.mode === 'IN', // Only IN params can be "required" in a form sense
23
+ details: [{ text: param.mode, class: `badge-${param.mode === 'IN' ? 'info' : param.mode === 'OUT' ? 'success' : 'warning'}` }],
24
+ }))
25
+ );
26
+ </script>
27
+
28
+ <div class="space-y-3 py-2">
29
+ {#if func.description}
30
+ <RLDetailRow name="Description" typeInfo={func.description} nameClass="font-semibold opacity-80" typeInfoClass="italic text-sm" />
31
+ {/if}
32
+
33
+ <RLDetailRow name="Kind" typeInfo={func.kind} nameClass="font-semibold opacity-80" />
34
+ <RLDetailRow name="Return Type" typeInfo={func.returnType || 'void'} nameClass="font-semibold opacity-80" />
35
+ <RLDetailRow name="Is Strict" typeInfo={func.isStrict ? 'Yes' : 'No'} nameClass="font-semibold opacity-80" />
36
+
37
+ {#if func.kind === 'TRIGGER' && func.triggerData}
38
+ <RLMetadataList title="Trigger Details" items={[
39
+ { name: 'Timing', typeInfo: func.triggerData.timing },
40
+ { name: 'Events', typeInfo: func.triggerData.events.join(', ') },
41
+ { name: 'Target', typeInfo: `${func.triggerData.targetTableSchema}.${func.triggerData.targetTableName}` },
42
+ ]} />
43
+ {/if}
44
+
45
+ <RLMetadataList items={parameterItems} title="Parameters" emptyText="No parameters." />
46
+
47
+ {#if (func.kind === 'FUNCTION' || func.kind === 'PROCEDURE') && onOpenApiModal}
48
+ <div class="mt-4 pt-3 border-t border-base-300/30">
49
+ <RLApiInterface
50
+ schemaName={func.schema}
51
+ resourceName={func.name}
52
+ resourceType={'function'}
53
+ columns={[]}
54
+ onOpenModal={(opParams) => onOpenApiModal({
55
+ ...opParams,
56
+ schemaName: func.schema,
57
+ resourceName: func.name,
58
+ resourceType: 'function',
59
+ options: { functionParams: func.parameters } // Passing RLFunctionParameter[]
60
+ })}
61
+ />
62
+ </div>
63
+ {/if}
64
+ </div>
@@ -0,0 +1,13 @@
1
+ import type { RLApiInterfaceActionParams, RLFunctionMetadata } from '../stores/explorer.svelte';
2
+ type $$ComponentProps = {
3
+ func: RLFunctionMetadata;
4
+ onOpenApiModal: (params: RLApiInterfaceActionParams & {
5
+ schemaName: string;
6
+ resourceName: string;
7
+ resourceType: 'function';
8
+ options: any;
9
+ }) => void;
10
+ };
11
+ declare const RlFunctionDisplay: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type RlFunctionDisplay = ReturnType<typeof RlFunctionDisplay>;
13
+ export default RlFunctionDisplay;