metal-orm 1.0.8 → 1.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.
Files changed (153) hide show
  1. package/README.md +12 -1
  2. package/dist/decorators/index.cjs +2564 -0
  3. package/dist/decorators/index.cjs.map +1 -0
  4. package/dist/decorators/index.d.cts +53 -0
  5. package/dist/decorators/index.d.ts +53 -0
  6. package/dist/decorators/index.js +2530 -0
  7. package/dist/decorators/index.js.map +1 -0
  8. package/dist/index.cjs +4227 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.cts +701 -0
  11. package/dist/index.d.ts +701 -0
  12. package/dist/index.js +4131 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/select-654m4qy8.d.cts +1522 -0
  15. package/dist/select-654m4qy8.d.ts +1522 -0
  16. package/package.json +27 -20
  17. package/src/codegen/typescript.ts +405 -393
  18. package/src/core/ast/aggregate-functions.ts +30 -0
  19. package/src/core/ast/builders.ts +43 -0
  20. package/src/core/ast/expression-builders.ts +310 -0
  21. package/src/core/ast/expression-nodes.ts +211 -0
  22. package/src/core/ast/expression-visitor.ts +99 -0
  23. package/src/core/ast/expression.ts +5 -0
  24. package/src/{utils → core/ast}/join-node.ts +20 -20
  25. package/src/{ast → core/ast}/join.ts +18 -18
  26. package/src/{ast → core/ast}/query.ts +113 -113
  27. package/src/core/ast/window-functions.ts +140 -0
  28. package/src/{dialect → core/dialect}/abstract.ts +94 -94
  29. package/src/{dialect → core/dialect}/mssql/index.ts +31 -31
  30. package/src/{dialect → core/dialect}/mysql/index.ts +31 -31
  31. package/src/{dialect → core/dialect}/postgres/index.ts +45 -45
  32. package/src/{dialect → core/dialect}/sqlite/index.ts +45 -45
  33. package/src/{constants → core/sql}/sql-operator-config.ts +39 -39
  34. package/src/decorators/bootstrap.ts +126 -0
  35. package/src/decorators/column.ts +78 -0
  36. package/src/decorators/entity.ts +36 -0
  37. package/src/decorators/index.ts +4 -0
  38. package/src/decorators/relations.ts +107 -0
  39. package/src/global.d.ts +1 -0
  40. package/src/index.ts +22 -22
  41. package/src/orm/db-executor.ts +11 -0
  42. package/src/orm/domain-event-bus.ts +52 -0
  43. package/src/{runtime → orm}/entity-meta.ts +52 -52
  44. package/src/orm/entity-metadata.ts +140 -0
  45. package/src/{runtime → orm}/entity.ts +252 -252
  46. package/src/{runtime → orm}/execute.ts +36 -36
  47. package/src/{runtime → orm}/hydration.ts +103 -103
  48. package/src/orm/identity-map.ts +37 -0
  49. package/src/{runtime → orm}/lazy-batch.ts +205 -205
  50. package/src/orm/orm-context.ts +154 -0
  51. package/src/orm/relation-change-processor.ts +140 -0
  52. package/src/{runtime → orm}/relations/belongs-to.ts +92 -92
  53. package/src/{runtime → orm}/relations/has-many.ts +111 -111
  54. package/src/{runtime → orm}/relations/many-to-many.ts +149 -149
  55. package/src/orm/runtime-types.ts +39 -0
  56. package/src/orm/transaction-runner.ts +17 -0
  57. package/src/orm/unit-of-work.ts +232 -0
  58. package/src/{builder/operations → query-builder}/column-selector.ts +78 -78
  59. package/src/{builder → query-builder}/delete-query-state.ts +38 -42
  60. package/src/{builder → query-builder}/delete.ts +46 -57
  61. package/src/{builder → query-builder}/hydration-manager.ts +87 -87
  62. package/src/{builder → query-builder}/hydration-planner.ts +182 -182
  63. package/src/{builder → query-builder}/insert-query-state.ts +51 -62
  64. package/src/{builder → query-builder}/insert.ts +48 -59
  65. package/src/{builder → query-builder}/query-ast-service.ts +208 -226
  66. package/src/{utils → query-builder}/raw-column-parser.ts +32 -32
  67. package/src/{builder → query-builder}/relation-conditions.ts +112 -112
  68. package/src/{builder/operations → query-builder}/relation-manager.ts +82 -82
  69. package/src/{builder → query-builder}/relation-projection-helper.ts +101 -101
  70. package/src/{builder → query-builder}/relation-service.ts +284 -284
  71. package/src/{builder → query-builder}/relation-types.ts +21 -21
  72. package/src/{builder → query-builder}/relation-utils.ts +12 -12
  73. package/src/{builder → query-builder}/select-query-builder-deps.ts +112 -94
  74. package/src/{builder → query-builder}/select-query-state.ts +179 -179
  75. package/src/{builder → query-builder}/select.ts +78 -69
  76. package/src/{builder → query-builder}/update-query-state.ts +55 -59
  77. package/src/{builder → query-builder}/update.ts +50 -61
  78. package/src/schema/column.ts +25 -25
  79. package/src/schema/relation.ts +116 -116
  80. package/src/schema/table.ts +34 -34
  81. package/src/schema/types.ts +76 -76
  82. package/.github/workflows/publish-metal-orm.yml +0 -38
  83. package/ROADMAP.md +0 -125
  84. package/docs/CHANGES.md +0 -104
  85. package/docs/advanced-features.md +0 -176
  86. package/docs/api-reference.md +0 -31
  87. package/docs/dml-operations.md +0 -156
  88. package/docs/getting-started.md +0 -171
  89. package/docs/hydration.md +0 -115
  90. package/docs/index.md +0 -36
  91. package/docs/multi-dialect-support.md +0 -59
  92. package/docs/query-builder.md +0 -135
  93. package/docs/runtime.md +0 -105
  94. package/docs/schema-definition.md +0 -112
  95. package/metadata.json +0 -5
  96. package/playground/api/playground-api.ts +0 -94
  97. package/playground/index.html +0 -15
  98. package/playground/src/App.css +0 -1
  99. package/playground/src/App.tsx +0 -114
  100. package/playground/src/components/CodeDisplay.tsx +0 -43
  101. package/playground/src/components/QueryExecutor.tsx +0 -189
  102. package/playground/src/components/ResultsTable.tsx +0 -67
  103. package/playground/src/components/ResultsTabs.tsx +0 -105
  104. package/playground/src/components/ScenarioList.tsx +0 -56
  105. package/playground/src/components/logo.svg +0 -45
  106. package/playground/src/data/scenarios.ts +0 -2
  107. package/playground/src/main.tsx +0 -9
  108. package/playground/src/services/PlaygroundApiService.ts +0 -60
  109. package/postcss.config.cjs +0 -5
  110. package/sql_sql-ansi-cheatsheet-2025.md +0 -264
  111. package/src/ast/expression.ts +0 -658
  112. package/src/builder/operations/cte-manager.ts +0 -34
  113. package/src/builder/operations/filter-manager.ts +0 -68
  114. package/src/builder/operations/join-manager.ts +0 -36
  115. package/src/builder/operations/pagination-manager.ts +0 -36
  116. package/src/playground/features/playground/api/types.ts +0 -16
  117. package/src/playground/features/playground/clients/MockClient.ts +0 -17
  118. package/src/playground/features/playground/clients/SqliteClient.ts +0 -57
  119. package/src/playground/features/playground/common/IDatabaseClient.ts +0 -10
  120. package/src/playground/features/playground/data/scenarios/aggregation.ts +0 -36
  121. package/src/playground/features/playground/data/scenarios/basics.ts +0 -25
  122. package/src/playground/features/playground/data/scenarios/edge_cases.ts +0 -57
  123. package/src/playground/features/playground/data/scenarios/filtering.ts +0 -94
  124. package/src/playground/features/playground/data/scenarios/hydration.ts +0 -27
  125. package/src/playground/features/playground/data/scenarios/index.ts +0 -29
  126. package/src/playground/features/playground/data/scenarios/ordering.ts +0 -25
  127. package/src/playground/features/playground/data/scenarios/pagination.ts +0 -16
  128. package/src/playground/features/playground/data/scenarios/relationships.ts +0 -75
  129. package/src/playground/features/playground/data/scenarios/types.ts +0 -70
  130. package/src/playground/features/playground/data/schema.ts +0 -91
  131. package/src/playground/features/playground/data/seed.ts +0 -104
  132. package/src/playground/features/playground/services/QueryExecutionService.ts +0 -121
  133. package/src/runtime/orm-context.ts +0 -539
  134. package/tests/belongs-to-many.test.ts +0 -57
  135. package/tests/between.test.ts +0 -43
  136. package/tests/case-expression.test.ts +0 -58
  137. package/tests/complex-exists.test.ts +0 -230
  138. package/tests/cte.test.ts +0 -118
  139. package/tests/dml.test.ts +0 -206
  140. package/tests/exists.test.ts +0 -127
  141. package/tests/like.test.ts +0 -33
  142. package/tests/orm-runtime.test.ts +0 -254
  143. package/tests/postgres.test.ts +0 -30
  144. package/tests/right-join.test.ts +0 -89
  145. package/tests/subquery-having.test.ts +0 -193
  146. package/tests/window-function.test.ts +0 -151
  147. package/tsconfig.json +0 -30
  148. package/tsup.config.ts +0 -10
  149. package/vite.config.ts +0 -22
  150. package/vitest.config.ts +0 -14
  151. /package/src/{constants → core/sql}/sql.ts +0 -0
  152. /package/src/{runtime → orm}/als.ts +0 -0
  153. /package/src/{utils → query-builder}/relation-alias.ts +0 -0
@@ -1,114 +0,0 @@
1
- import { useState, useEffect, useMemo } from 'react';
2
- import { MantineProvider, AppShell, Burger, Group, Title, Text, ActionIcon, useMantineTheme } from '@mantine/core';
3
- import { useDisclosure } from '@mantine/hooks';
4
- import { SCENARIOS, type Scenario } from './data/scenarios';
5
- import { PlaygroundApiService } from './services/PlaygroundApiService';
6
- import { ScenarioList } from './components/ScenarioList';
7
- import { QueryExecutor } from './components/QueryExecutor';
8
- import '@mantine/core/styles.css';
9
- import './App.css'; // Keeping for custom overrides if needed, but will likely remove
10
-
11
- function App() {
12
- const [selectedScenario, setSelectedScenario] = useState<Scenario | null>(null);
13
- const [apiReady, setApiReady] = useState(false);
14
- const [statusMessage, setStatusMessage] = useState<string | null>(null);
15
- const [opened, { toggle }] = useDisclosure();
16
-
17
- const queryService = useMemo(() => new PlaygroundApiService(), []);
18
-
19
- useEffect(() => {
20
- let isMounted = true;
21
- let timerId: ReturnType<typeof setTimeout> | null = null;
22
-
23
- const pollStatus = async () => {
24
- const status = await queryService.getStatus();
25
- if (!isMounted) return;
26
-
27
- if (status.error) {
28
- setStatusMessage(status.error);
29
- } else {
30
- setStatusMessage(null);
31
- }
32
-
33
- if (status.ready) {
34
- setApiReady(true);
35
- timerId && clearTimeout(timerId);
36
- return;
37
- }
38
-
39
- timerId = setTimeout(pollStatus, 250);
40
- };
41
-
42
- pollStatus();
43
-
44
- return () => {
45
- isMounted = false;
46
- if (timerId) {
47
- clearTimeout(timerId);
48
- }
49
- };
50
- }, [queryService]);
51
-
52
- // Auto-select first scenario when API is ready
53
- useEffect(() => {
54
- if (apiReady && !selectedScenario && SCENARIOS.length > 0) {
55
- setSelectedScenario(SCENARIOS[0]);
56
- }
57
- }, [apiReady, selectedScenario]);
58
-
59
- const handleScenarioSelect = (scenario: Scenario) => {
60
- setSelectedScenario(scenario);
61
- if (window.innerWidth < 768) {
62
- toggle(); // Close sidebar on mobile selection
63
- }
64
- };
65
-
66
- if (!apiReady) {
67
- return (
68
- <MantineProvider defaultColorScheme="dark">
69
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100vh', flexDirection: 'column' }}>
70
- <Title order={2}>Initializing Metal ORM Playground</Title>
71
- <Text c="dimmed">Waiting for the playground API to become ready...</Text>
72
- {statusMessage && <Text c="red">Error: {statusMessage}</Text>}
73
- </div>
74
- </MantineProvider>
75
- );
76
- }
77
-
78
- return (
79
- <MantineProvider defaultColorScheme="dark">
80
- <AppShell
81
- header={{ height: 60 }}
82
- navbar={{ width: 300, breakpoint: 'sm', collapsed: { mobile: !opened } }}
83
- padding="md"
84
- >
85
- <AppShell.Header>
86
- <Group h="100%" px="md">
87
- <Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
88
- <Group justify="space-between" style={{ flex: 1 }}>
89
- <Title order={3}>⚡ Metal ORM Playground</Title>
90
- <Text size="sm" c="dimmed" visibleFrom="sm">Explore and test ORM query scenarios</Text>
91
- </Group>
92
- </Group>
93
- </AppShell.Header>
94
-
95
- <AppShell.Navbar p="md">
96
- <ScenarioList
97
- scenarios={SCENARIOS}
98
- selectedId={selectedScenario?.id || null}
99
- onSelect={handleScenarioSelect}
100
- />
101
- </AppShell.Navbar>
102
-
103
- <AppShell.Main>
104
- <QueryExecutor
105
- scenario={selectedScenario}
106
- queryService={queryService}
107
- />
108
- </AppShell.Main>
109
- </AppShell>
110
- </MantineProvider>
111
- );
112
- }
113
-
114
- export default App;
@@ -1,43 +0,0 @@
1
- import React from 'react';
2
- import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
3
- import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
4
- import { ScrollArea } from '@mantine/core';
5
-
6
- interface CodeDisplayProps {
7
- code: string;
8
- language?: 'sql' | 'typescript';
9
- title?: string;
10
- }
11
-
12
- /**
13
- * Component responsible for displaying syntax-highlighted code
14
- * Follows SRP by handling only code display formatting
15
- */
16
- export const CodeDisplay: React.FC<CodeDisplayProps> = ({
17
- code,
18
- language = 'sql',
19
- title
20
- }) => {
21
- return (
22
- <ScrollArea bg="#282c34">
23
- <SyntaxHighlighter
24
- language={language}
25
- style={oneDark}
26
- customStyle={{
27
- margin: 0,
28
- padding: '1.25rem',
29
- background: 'transparent',
30
- fontSize: '0.9rem',
31
- lineHeight: '1.6'
32
- }}
33
- codeTagProps={{
34
- style: {
35
- fontFamily: 'JetBrains Mono, Fira Code, monospace'
36
- }
37
- }}
38
- >
39
- {code}
40
- </SyntaxHighlighter>
41
- </ScrollArea>
42
- );
43
- };
@@ -1,189 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Card, Button, Title, Text, Tabs, Loader, Group, Stack, Badge, ActionIcon, CopyButton, Tooltip } from '@mantine/core';
3
- import { IconPlayerPlay, IconCode, IconDatabase, IconCheck, IconCopy } from '@tabler/icons-react'; // Assuming tabler icons are available or we use text
4
- import type { Scenario } from '../data/scenarios';
5
- import type { QueryExecutionResult } from '@orm/playground/features/playground/api/types';
6
- import { CodeDisplay } from './CodeDisplay';
7
- import { ResultsTabs } from './ResultsTabs';
8
- import { PlaygroundApiService } from '../services/PlaygroundApiService';
9
-
10
- const describeBinding = (value: unknown): { display: string; type: string } => {
11
- if (value === null) {
12
- return { display: 'null', type: 'null' };
13
- }
14
- if (typeof value === 'undefined') {
15
- return { display: 'undefined', type: 'undefined' };
16
- }
17
- if (typeof value === 'string') {
18
- return { display: `"${value}"`, type: 'string' };
19
- }
20
- if (typeof value === 'number') {
21
- return { display: value.toString(), type: Number.isInteger(value) ? 'integer' : 'number' };
22
- }
23
- if (typeof value === 'boolean') {
24
- return { display: value ? 'true' : 'false', type: 'boolean' };
25
- }
26
- if (Array.isArray(value)) {
27
- return { display: JSON.stringify(value), type: 'array' };
28
- }
29
- return { display: JSON.stringify(value), type: typeof value };
30
- };
31
-
32
- const BindingsDisplay: React.FC<{ params: unknown[] }> = ({ params }) => {
33
- const hasParams = Array.isArray(params) && params.length > 0;
34
-
35
- return (
36
- <div
37
- style={{
38
- border: '1px solid var(--mantine-color-dark-5)',
39
- borderRadius: 'var(--mantine-radius-md)',
40
- padding: 'var(--mantine-spacing-md)',
41
- background: 'var(--mantine-color-dark-8)'
42
- }}
43
- >
44
- <Group justify="space-between" mb="sm">
45
- <Text fw={500}>Bindings</Text>
46
- {hasParams && (
47
- <CopyButton value={JSON.stringify(params, null, 2)}>
48
- {({ copied, copy }) => (
49
- <Tooltip label={copied ? 'Copied' : 'Copy JSON'} withArrow>
50
- <ActionIcon variant="subtle" color={copied ? 'teal' : 'blue'} onClick={copy}>
51
- {copied ? <IconCheck size={14} /> : <IconCopy size={14} />}
52
- </ActionIcon>
53
- </Tooltip>
54
- )}
55
- </CopyButton>
56
- )}
57
- </Group>
58
- {!hasParams && <Text c="dimmed" fz="sm">Query has no parameter bindings.</Text>}
59
- {hasParams && (
60
- <Stack gap="xs">
61
- {params.map((value, index) => {
62
- const { display, type } = describeBinding(value);
63
- return (
64
- <Group
65
- key={`${index}-${String(value)}`}
66
- justify="space-between"
67
- style={{
68
- border: '1px solid var(--mantine-color-dark-5)',
69
- borderRadius: 'var(--mantine-radius-sm)',
70
- padding: 'var(--mantine-spacing-xs)'
71
- }}
72
- >
73
- <Badge variant="light" color="grape">#{index + 1}</Badge>
74
- <div style={{ textAlign: 'right' }}>
75
- <Text style={{ fontFamily: 'var(--mantine-font-family-monospace)' }}>
76
- {display}
77
- </Text>
78
- <Text fz="xs" c="dimmed">
79
- {type.toUpperCase()}
80
- </Text>
81
- </div>
82
- </Group>
83
- );
84
- })}
85
- </Stack>
86
- )}
87
- </div>
88
- );
89
- };
90
-
91
- interface QueryExecutorProps {
92
- scenario: Scenario | null;
93
- queryService: PlaygroundApiService;
94
- }
95
-
96
- /**
97
- * Component responsible for executing queries and displaying results
98
- * Follows SRP by coordinating query execution and result display
99
- */
100
- export const QueryExecutor: React.FC<QueryExecutorProps> = ({
101
- scenario,
102
- queryService
103
- }) => {
104
- const [result, setResult] = useState<QueryExecutionResult | null>(null);
105
- const [isLoading, setIsLoading] = useState(false);
106
-
107
- useEffect(() => {
108
- if (scenario) {
109
- executeQuery();
110
- }
111
- }, [scenario]);
112
-
113
- const executeQuery = async () => {
114
- if (!scenario) return;
115
-
116
- setIsLoading(true);
117
- const executionResult = await queryService.executeScenario(scenario.id);
118
- setResult(executionResult);
119
- setIsLoading(false);
120
- };
121
-
122
- if (!scenario) {
123
- return (
124
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', color: 'var(--mantine-color-dimmed)' }}>
125
- <Text>Select a scenario from the list to execute</Text>
126
- </div>
127
- );
128
- }
129
-
130
- return (
131
- <Stack gap="lg">
132
- <div>
133
- <Group justify="space-between" align="start" mb="md">
134
- <div>
135
- <Title order={2}>{scenario.title}</Title>
136
- <Text c="dimmed" mt="xs">{scenario.description}</Text>
137
- </div>
138
- <Button
139
- onClick={executeQuery}
140
- loading={isLoading}
141
- leftSection={<span>▶</span>}
142
- variant="gradient"
143
- gradient={{ from: 'indigo', to: 'cyan' }}
144
- >
145
- Re-execute Query
146
- </Button>
147
- </Group>
148
- </div>
149
-
150
- {result && (
151
- <>
152
- <Card withBorder shadow="sm" radius="md" p={0}>
153
- <Tabs defaultValue="sql">
154
- <Tabs.List>
155
- <Tabs.Tab value="sql" leftSection={<span>SQL</span>}>Generated SQL</Tabs.Tab>
156
- <Tabs.Tab value="typescript" leftSection={<span>TS</span>}>TypeScript</Tabs.Tab>
157
- </Tabs.List>
158
-
159
- <Tabs.Panel value="sql">
160
- <Stack gap="sm" p="md">
161
- <CodeDisplay code={result.sql} language="sql" />
162
- <BindingsDisplay params={result.params} />
163
- </Stack>
164
- </Tabs.Panel>
165
-
166
- <Tabs.Panel value="typescript">
167
- <CodeDisplay code={result.typescriptCode} language="typescript" />
168
- </Tabs.Panel>
169
- </Tabs>
170
- </Card>
171
-
172
- <ResultsTabs
173
- results={result.results}
174
- hydratedResults={result.hydratedResults}
175
- executionTime={result.executionTime}
176
- error={result.error}
177
- />
178
- </>
179
- )}
180
-
181
- {isLoading && (
182
- <Group justify="center" p="xl">
183
- <Loader type="dots" />
184
- <Text>Executing query...</Text>
185
- </Group>
186
- )}
187
- </Stack>
188
- );
189
- };
@@ -1,67 +0,0 @@
1
- import React from 'react';
2
- import { Table, Text, Alert, ScrollArea } from '@mantine/core';
3
- import type { QueryResult } from '@orm/playground/features/playground/common/IDatabaseClient';
4
-
5
- interface ResultsTableProps {
6
- results: QueryResult[];
7
- executionTime?: number;
8
- error?: string | null;
9
- }
10
-
11
- /**
12
- * Component responsible for displaying query results in a table
13
- * Follows SRP by handling only results visualization
14
- */
15
- export const ResultsTable: React.FC<ResultsTableProps> = ({
16
- results,
17
- executionTime,
18
- error
19
- }) => {
20
- if (error) {
21
- return (
22
- <Alert variant="light" color="red" title="Error">
23
- {error}
24
- </Alert>
25
- );
26
- }
27
-
28
- if (results.length === 0 || !results[0] || results[0].values.length === 0) {
29
- return (
30
- <div style={{ padding: '2rem', textAlign: 'center', color: 'var(--mantine-color-dimmed)' }}>
31
- <Text>No results returned</Text>
32
- </div>
33
- );
34
- }
35
-
36
- const { columns, values } = results[0];
37
-
38
- return (
39
- <div>
40
- <ScrollArea>
41
- <Table striped highlightOnHover horizontalSpacing="md" verticalSpacing="sm">
42
- <Table.Thead>
43
- <Table.Tr>
44
- {columns.map((col, idx) => (
45
- <Table.Th key={idx} style={{ whiteSpace: 'nowrap' }}>{col}</Table.Th>
46
- ))}
47
- </Table.Tr>
48
- </Table.Thead>
49
- <Table.Tbody>
50
- {values.map((row, rowIdx) => (
51
- <Table.Tr key={rowIdx}>
52
- {row.map((cell, cellIdx) => (
53
- <Table.Td key={cellIdx}>
54
- {cell === null ? <Text span c="dimmed" fs="italic">NULL</Text> : String(cell)}
55
- </Table.Td>
56
- ))}
57
- </Table.Tr>
58
- ))}
59
- </Table.Tbody>
60
- </Table>
61
- </ScrollArea>
62
- <div style={{ padding: '0.5rem 1rem', borderTop: '1px solid var(--mantine-color-default-border)', backgroundColor: 'var(--mantine-color-body)' }}>
63
- <Text size="xs" c="dimmed">{values.length} row{values.length !== 1 ? 's' : ''}</Text>
64
- </div>
65
- </div>
66
- );
67
- };
@@ -1,105 +0,0 @@
1
- import React from 'react';
2
- import { Tabs, Card, Text, Badge, Group, Alert, Code, ScrollArea } from '@mantine/core';
3
- import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
4
- import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
5
- import type { QueryResult } from '@orm/playground/features/playground/common/IDatabaseClient';
6
- import { ResultsTable } from './ResultsTable';
7
-
8
- /**
9
- * Converts tabular results to JSON array of objects
10
- */
11
- const convertTabularToJson = (results: QueryResult[]): Record<string, any>[] => {
12
- if (!results.length || !results[0].values.length) return [];
13
-
14
- const { columns, values } = results[0];
15
- return values.map(row => {
16
- const obj: Record<string, any> = {};
17
- columns.forEach((col, idx) => {
18
- obj[col] = row[idx];
19
- });
20
- return obj;
21
- });
22
- };
23
-
24
- interface ResultsTabsProps {
25
- results: QueryResult[];
26
- hydratedResults?: Record<string, any>[];
27
- executionTime?: number;
28
- error?: string | null;
29
- }
30
-
31
- /**
32
- * Component that displays query results in tabbed interface with Table and JSON views
33
- * Follows SRP by coordinating result display modes
34
- */
35
- export const ResultsTabs: React.FC<ResultsTabsProps> = ({
36
- results,
37
- hydratedResults,
38
- executionTime,
39
- error
40
- }) => {
41
- const hasResults = results.length > 0 && results[0].values.length > 0;
42
- const hasHydratedResults = hydratedResults && hydratedResults.length > 0;
43
-
44
- // Use hydrated results if available, otherwise convert tabular data to JSON
45
- const jsonData = hasHydratedResults ? hydratedResults : convertTabularToJson(results);
46
- const jsonTitle = hasHydratedResults ? 'Hydrated Results' : 'Results as JSON';
47
-
48
- if (error) {
49
- return (
50
- <Alert variant="light" color="red" title="Error" mt="md">
51
- {error}
52
- </Alert>
53
- );
54
- }
55
-
56
- return (
57
- <Card withBorder shadow="sm" radius="md" p={0}>
58
- <Tabs defaultValue="table">
59
- <Group justify="space-between" px="md" py="xs" style={{ borderBottom: '1px solid var(--mantine-color-default-border)' }}>
60
- <Tabs.List style={{ borderBottom: 'none' }}>
61
- <Tabs.Tab value="table">Table</Tabs.Tab>
62
- {hasResults && <Tabs.Tab value="json">JSON</Tabs.Tab>}
63
- </Tabs.List>
64
- {executionTime !== undefined && (
65
- <Badge variant="light" color="green" size="lg">
66
- {executionTime.toFixed(2)}ms
67
- </Badge>
68
- )}
69
- </Group>
70
-
71
- <Tabs.Panel value="table">
72
- <ResultsTable
73
- results={results}
74
- executionTime={undefined}
75
- error={null}
76
- />
77
- </Tabs.Panel>
78
-
79
- {hasResults && (
80
- <Tabs.Panel value="json">
81
- <Group justify="space-between" px="md" py="xs" bg="var(--mantine-color-dark-6)">
82
- <Text size="sm" fw={500}>{jsonTitle}</Text>
83
- <Text size="xs" c="dimmed">{jsonData.length} object{jsonData.length !== 1 ? 's' : ''}</Text>
84
- </Group>
85
- <ScrollArea h={400} bg="#282c34">
86
- <SyntaxHighlighter
87
- language="json"
88
- style={oneDark}
89
- customStyle={{
90
- margin: 0,
91
- padding: '1.25rem',
92
- background: 'transparent',
93
- fontSize: '0.85rem',
94
- lineHeight: '1.5'
95
- }}
96
- >
97
- {JSON.stringify(jsonData, null, 2)}
98
- </SyntaxHighlighter>
99
- </ScrollArea>
100
- </Tabs.Panel>
101
- )}
102
- </Tabs>
103
- </Card>
104
- );
105
- };
@@ -1,56 +0,0 @@
1
- import React from 'react';
2
- import { NavLink, Stack, Text, ScrollArea } from '@mantine/core';
3
- import type { Scenario } from '../data/scenarios';
4
-
5
- interface ScenarioListProps {
6
- scenarios: Scenario[];
7
- selectedId: string | null;
8
- onSelect: (scenario: Scenario) => void;
9
- }
10
-
11
- /**
12
- * Component responsible for displaying the list of scenarios
13
- * Follows SRP by handling only scenario list rendering and selection
14
- */
15
- export const ScenarioList: React.FC<ScenarioListProps> = ({
16
- scenarios,
17
- selectedId,
18
- onSelect
19
- }) => {
20
- // Group scenarios by category
21
- const categorizedScenarios = scenarios.reduce((acc, scenario) => {
22
- if (!acc[scenario.category]) {
23
- acc[scenario.category] = [];
24
- }
25
- acc[scenario.category].push(scenario);
26
- return acc;
27
- }, {} as Record<string, Scenario[]>);
28
-
29
- return (
30
- <ScrollArea h="calc(100vh - 80px)">
31
- <Stack gap="md">
32
- {Object.entries(categorizedScenarios).map(([category, items]) => (
33
- <div key={category}>
34
- <Text size="xs" fw={700} c="dimmed" tt="uppercase" mb="xs" px="xs">
35
- {category}
36
- </Text>
37
- <Stack gap={2}>
38
- {items.map((scenario) => (
39
- <NavLink
40
- key={scenario.id}
41
- label={scenario.title}
42
- description={scenario.description}
43
- active={selectedId === scenario.id}
44
- onClick={() => onSelect(scenario)}
45
- variant="light"
46
- color="indigo"
47
- style={{ borderRadius: 'var(--mantine-radius-md)' }}
48
- />
49
- ))}
50
- </Stack>
51
- </div>
52
- ))}
53
- </Stack>
54
- </ScrollArea>
55
- );
56
- };
@@ -1,45 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
2
- <defs>
3
- <linearGradient id="metalGrad" x1="0%" y1="0%" x2="100%" y2="100%">
4
- <stop offset="0%" style="stop-color:#e0e0e0;stop-opacity:1" />
5
- <stop offset="50%" style="stop-color:#909090;stop-opacity:1" />
6
- <stop offset="100%" style="stop-color:#404040;stop-opacity:1" />
7
- </linearGradient>
8
-
9
- <linearGradient id="speedGrad" x1="0%" y1="0%" x2="100%" y2="0%">
10
- <stop offset="0%" style="stop-color:#ff8c00;stop-opacity:1" />
11
- <stop offset="100%" style="stop-color:#ffeb3b;stop-opacity:1" />
12
- </linearGradient>
13
-
14
- <filter id="dropShadow" x="-20%" y="-20%" width="140%" height="140%">
15
- <feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
16
- <feOffset dx="2" dy="2" result="offsetblur"/>
17
- <feComponentTransfer>
18
- <feFuncA type="linear" slope="0.5"/>
19
- </feComponentTransfer>
20
- <feMerge>
21
- <feMergeNode in="offsetblur"/>
22
- <feMergeNode in="SourceGraphic"/>
23
- </feMerge>
24
- </filter>
25
- </defs>
26
-
27
- <path d="M100,20 L130,10 L145,35 L175,35 L180,65 L200,80 L185,105 L200,130 L180,145 L175,175 L145,175 L130,200 L100,190 L70,200 L55,175 L25,175 L20,145 L0,130 L15,105 L0,80 L20,65 L25,35 L55,35 L70,10 Z"
28
- fill="url(#metalGrad)"
29
- stroke="#333"
30
- stroke-width="2"
31
- filter="url(#dropShadow)" />
32
-
33
- <circle cx="100" cy="105" r="55" fill="#222" stroke="#555" stroke-width="2" />
34
-
35
- <ellipse cx="100" cy="85" rx="35" ry="10" fill="none" stroke="#ccc" stroke-width="3" />
36
- <path d="M65,85 v30 a35,10 0 0,0 70,0 v-30" fill="none" stroke="#ccc" stroke-width="3" />
37
- <path d="M65,100 v15 a35,10 0 0,0 70,0 v-15" fill="none" stroke="#ccc" stroke-width="3" opacity="0.5" />
38
-
39
- <path d="M120,55 L85,105 L105,105 L80,155 L135,95 L110,95 Z"
40
- fill="url(#speedGrad)"
41
- stroke="#fff"
42
- stroke-width="1"
43
- transform="translate(-5, 0)" />
44
-
45
- </svg>
@@ -1,2 +0,0 @@
1
- export { SCENARIOS } from '@orm/playground/features/playground/data/scenarios';
2
- export type { Scenario } from '@orm/playground/features/playground/data/scenarios';
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom/client';
3
- import App from './App';
4
-
5
- ReactDOM.createRoot(document.getElementById('root')!).render(
6
- <React.StrictMode>
7
- <App />
8
- </React.StrictMode>,
9
- );