diff --git a/packages/evidence/index.js b/packages/evidence/index.js index e69de29bb2..3cdf9e4a1f 100644 --- a/packages/evidence/index.js +++ b/packages/evidence/index.js @@ -0,0 +1 @@ +export { buildEvidence, runEvidence, sourcesEvidence } from './programmatic.js'; diff --git a/packages/evidence/package.json b/packages/evidence/package.json index bedfd7bac2..2b0ea764c7 100644 --- a/packages/evidence/package.json +++ b/packages/evidence/package.json @@ -27,7 +27,9 @@ }, "files": [ "template", - "cli.js" + "cli.js", + "index.js", + "programmatic.js" ], "devDependencies": { "@evidence-dev/component-utilities": "workspace:*", @@ -67,4 +69,4 @@ "engines": { "node": ">=18" } -} \ No newline at end of file +} diff --git a/packages/evidence/programmatic.js b/packages/evidence/programmatic.js new file mode 100644 index 0000000000..a3ac02251e --- /dev/null +++ b/packages/evidence/programmatic.js @@ -0,0 +1,67 @@ +import { cli } from '@evidence-dev/sdk/legacy-compat'; + +const restoreEnv = (originalEnv) => { + for (const key of Object.keys(process.env)) { + if (!(key in originalEnv)) delete process.env[key]; + } + Object.assign(process.env, originalEnv); +}; + +/** + * Run an Evidence CLI command in-process without spawning a long-running server by default. + * @param {string[]} args + * @param {{cwd?: string, env?: Record}} [options] + * @returns {Promise} + */ +export const runEvidence = async (args = [], options = {}) => { + const originalArgv = process.argv; + const originalCwd = process.cwd(); + const originalEnv = { ...process.env }; + + try { + if (options.cwd) process.chdir(options.cwd); + if (options.env) Object.assign(process.env, options.env); + process.argv = ['node', 'evidence', ...args]; + await cli(); + } finally { + process.argv = originalArgv; + restoreEnv(originalEnv); + if (process.cwd() !== originalCwd) process.chdir(originalCwd); + } +}; + +const addFlag = (args, name, value) => { + if (value === undefined || value === false || value === null) return; + args.push(`--${name}`); + if (value !== true) args.push(String(value)); +}; + +/** + * Build an Evidence project to static output. + * @param {{strict?: boolean, debug?: boolean, cwd?: string, env?: Record}} [options] + * @returns {Promise} + */ +export const buildEvidence = async (options = {}) => { + const args = [options.strict ? 'build:strict' : 'build']; + addFlag(args, 'debug', options.debug); + await runEvidence(args, options); +}; + +/** + * Evaluate and materialize Evidence source queries. + * @param {{changed?: boolean, sources?: string | string[], queries?: string | string[], strict?: boolean, debug?: boolean, cwd?: string, env?: Record}} [options] + * @returns {Promise} + */ +export const sourcesEvidence = async (options = {}) => { + const args = ['sources']; + addFlag(args, 'changed', options.changed); + addFlag(args, 'strict', options.strict); + addFlag(args, 'debug', options.debug); + + const sources = Array.isArray(options.sources) ? options.sources.join(',') : options.sources; + const queries = Array.isArray(options.queries) ? options.queries.join(',') : options.queries; + addFlag(args, 'sources', sources); + addFlag(args, 'queries', queries); + + await runEvidence(args, options); +}; diff --git a/packages/lib/sdk/src/plugins/datasources/index.js b/packages/lib/sdk/src/plugins/datasources/index.js index a70b7cb1a5..40ace26757 100644 --- a/packages/lib/sdk/src/plugins/datasources/index.js +++ b/packages/lib/sdk/src/plugins/datasources/index.js @@ -3,4 +3,7 @@ export * from './loadSources.js'; export * from './loadSourcePlugins.js'; export * from './writeSourceConfig.js'; export * from './cli/edit/Options.js'; +export * from './Datasources.js'; +export * from './evalSources.js'; +export * from './updateManifest.js'; export { getDatasourceConfigAsEnvironmentVariables } from './loadSourceConfig.js'; diff --git a/sites/example-project/src/pages/api/[...route]/evidencemeta.json/+server.js b/sites/example-project/src/pages/api/[...route]/evidencemeta.json/+server.js index 3bee2da692..d157f6e39e 100644 --- a/sites/example-project/src/pages/api/[...route]/evidencemeta.json/+server.js +++ b/sites/example-project/src/pages/api/[...route]/evidencemeta.json/+server.js @@ -27,7 +27,9 @@ export const entries = async () => { /** @type {import("./$types").RequestHandler} */ export async function GET({ params: { route } }) { - if (route === '/settings') { + const normalizedRoute = route.replace(/^\/+/, ''); + + if (normalizedRoute === 'settings') { const queries = []; return json({ queries }); } @@ -37,9 +39,18 @@ export async function GET({ params: { route } }) { } else { routesDir = path.join('.evidence', 'template', 'src', 'pages'); } - const routePath = path.join(process.cwd(), routesDir, route, '+page.md'); - - const content = await fs.readFile(routePath, 'utf8'); + const routePath = path.join(process.cwd(), routesDir, normalizedRoute, '+page.md'); + + let content; + try { + content = await fs.readFile(routePath, 'utf8'); + } catch (e) { + // Non-markdown routes (for example +page.svelte) do not have query metadata files. + if (e instanceof Error && 'code' in e && e.code === 'ENOENT') { + return json({ queries: [] }); + } + throw e; + } const partialInjectedContent = preprocessor.injectPartials(content); const queries = preprocessor.extractQueries(partialInjectedContent); diff --git a/sites/example-project/src/pages/dashboards/+layout.js b/sites/example-project/src/pages/dashboards/+layout.js new file mode 100644 index 0000000000..d96e987872 --- /dev/null +++ b/sites/example-project/src/pages/dashboards/+layout.js @@ -0,0 +1,7 @@ +// Folder-level rendering policy for all dashboard routes. +// Sub-pages can still override these values in their own +page.js files. +const runtimeSSR = import.meta.env.EVIDENCE_RUNTIME_SSR === 'true'; + +export const ssr = true; +export const csr = true; +export const prerender = !runtimeSSR; diff --git a/sites/example-project/src/pages/dashboards/+page.md b/sites/example-project/src/pages/dashboards/+page.md new file mode 100644 index 0000000000..db3c1dfd1b --- /dev/null +++ b/sites/example-project/src/pages/dashboards/+page.md @@ -0,0 +1,18 @@ +--- +title: Dashboards (SSR + CSR POC) +--- + +This folder is configured with: + +- `ssr = true` +- `csr = true` +- `prerender = !import.meta.env.EVIDENCE_RUNTIME_SSR` + +Set `EVIDENCE_RUNTIME_SSR=true` to make this folder runtime-rendered. + +Without that env var, pages here are prerendered for static build compatibility. + +## Pages + +- [Realtime Dashboard](./realtime) +- [Monthly Snapshot (Per-page static override)](./monthly-snapshot) diff --git a/sites/example-project/src/pages/dashboards/monthly-snapshot/+page.js b/sites/example-project/src/pages/dashboards/monthly-snapshot/+page.js new file mode 100644 index 0000000000..81baf2465e --- /dev/null +++ b/sites/example-project/src/pages/dashboards/monthly-snapshot/+page.js @@ -0,0 +1,6 @@ +// Per-page override: this page is statically prerendered even though its parent +// dashboards folder defaults to runtime SSR. +export const prerender = true; +export const ssr = true; +export const csr = true; + diff --git a/sites/example-project/src/pages/dashboards/monthly-snapshot/+page.md b/sites/example-project/src/pages/dashboards/monthly-snapshot/+page.md new file mode 100644 index 0000000000..b99f5e5275 --- /dev/null +++ b/sites/example-project/src/pages/dashboards/monthly-snapshot/+page.md @@ -0,0 +1,8 @@ +--- +title: Monthly Snapshot (Static Override) +--- + +This page overrides its parent folder defaults with `prerender = true`. + +Use this pattern for dashboards that update on a schedule (for example, nightly) while other dashboard routes stay runtime SSR. + diff --git a/sites/example-project/src/pages/dashboards/realtime/+page.server.js b/sites/example-project/src/pages/dashboards/realtime/+page.server.js new file mode 100644 index 0000000000..b64792977e --- /dev/null +++ b/sites/example-project/src/pages/dashboards/realtime/+page.server.js @@ -0,0 +1,8 @@ +/** @type {import('./$types').PageServerLoad} */ +export const load = async ({ url }) => { + return { + pathname: url.pathname, + serverRenderedAt: new Date().toISOString() + }; +}; + diff --git a/sites/example-project/src/pages/dashboards/realtime/+page.svelte b/sites/example-project/src/pages/dashboards/realtime/+page.svelte new file mode 100644 index 0000000000..60deb016cb --- /dev/null +++ b/sites/example-project/src/pages/dashboards/realtime/+page.svelte @@ -0,0 +1,30 @@ + + +

Realtime Dashboard (Runtime SSR + CSR)

+

+ This page is rendered on the server for each request and then hydrated in the browser. +

+ +
    +
  • Route: {data.pathname}
  • +
  • Server rendered at: {data.serverRenderedAt}
  • +
  • Client hydrated at: {hydratedAt || 'hydrating...'}
  • +
  • Client clock (updates every second): {clientNow.toISOString()}
  • +
+ diff --git a/sites/example-project/src/pages/templated-pages/[category]/+page.js b/sites/example-project/src/pages/templated-pages/[category]/+page.js new file mode 100644 index 0000000000..ffbf43efe3 --- /dev/null +++ b/sites/example-project/src/pages/templated-pages/[category]/+page.js @@ -0,0 +1,2 @@ +export const prerender = false; + diff --git a/sites/example-project/src/pages/templated-pages/[category]/[item]/+page.js b/sites/example-project/src/pages/templated-pages/[category]/[item]/+page.js new file mode 100644 index 0000000000..ffbf43efe3 --- /dev/null +++ b/sites/example-project/src/pages/templated-pages/[category]/[item]/+page.js @@ -0,0 +1,2 @@ +export const prerender = false; + diff --git a/sites/example-project/src/pages/types/+page.md b/sites/example-project/src/pages/types/+page.md index e8903c633d..4485a532ab 100644 --- a/sites/example-project/src/pages/types/+page.md +++ b/sites/example-project/src/pages/types/+page.md @@ -73,10 +73,64 @@ SELECT # Nastier Test Values ```sql all_types -select - * -from test_all_types() -limit 2 +select * +from ( + select + 'row_a'::varchar as varchar, + X'53514C' as blob, + true as bool, + 12::tinyint as tinyint, + 1200::smallint as smallint, + 120000::int as int, + 1200000000::bigint as bigint, + 1234567890123456789::hugeint as hugeint, + 200::utinyint as utinyint, + 50000::usmallint as usmallint, + 3000000000::uinteger as uint, + 9000000000000000000::ubigint as ubigint, + 1.25::float as float, + 2.5::double as double, + date '2024-01-01' as date, + timestamp '2024-01-01 10:11:12' as timestamp, + cast('2024-01-01 10:11:12' as timestamp_s) as timestamp_s, + cast('2024-01-01 10:11:12' as timestamp_ms) as timestamp_ms, + cast('2024-01-01 10:11:12' as timestamp_ns) as timestamp_ns, + cast('2024-01-01 10:11:12+00:00' as timestamp with time zone) as timestamp_tz, + cast('10:11:12' as time) as time, + cast('10:11:12+00:00' as time with time zone) as time_tz, + cast(3.1 as decimal(4,1)) as dec_4_1, + cast(3.1415 as decimal(9,4)) as dec_9_4, + cast(3.141592 as decimal(18,6)) as dec_18_6, + cast(3.1415926535 as decimal(38,10)) as dec38_10 + union all + select + 'row_b'::varchar as varchar, + X'414243' as blob, + false as bool, + 24::tinyint as tinyint, + 2400::smallint as smallint, + 240000::int as int, + 2400000000::bigint as bigint, + 2234567890123456789::hugeint as hugeint, + 150::utinyint as utinyint, + 45000::usmallint as usmallint, + 2000000000::uinteger as uint, + 8000000000000000000::ubigint as ubigint, + 2.25::float as float, + 3.5::double as double, + date '2024-01-02' as date, + timestamp '2024-01-02 10:11:12' as timestamp, + cast('2024-01-02 10:11:12' as timestamp_s) as timestamp_s, + cast('2024-01-02 10:11:12' as timestamp_ms) as timestamp_ms, + cast('2024-01-02 10:11:12' as timestamp_ns) as timestamp_ns, + cast('2024-01-02 10:11:12+00:00' as timestamp with time zone) as timestamp_tz, + cast('11:11:12' as time) as time, + cast('11:11:12+00:00' as time with time zone) as time_tz, + cast(4.1 as decimal(4,1)) as dec_4_1, + cast(4.1415 as decimal(9,4)) as dec_9_4, + cast(4.141592 as decimal(18,6)) as dec_18_6, + cast(4.1415926535 as decimal(38,10)) as dec38_10 +) t ``` @@ -341,4 +395,3 @@ My dec(18,6) is **** /> My decimal(38,10) is **** - diff --git a/sites/example-project/src/pages/ui-components/button/+page.md b/sites/example-project/src/pages/ui-components/button/+page.md index 56de1ae2e6..392bd55ed7 100644 --- a/sites/example-project/src/pages/ui-components/button/+page.md +++ b/sites/example-project/src/pages/ui-components/button/+page.md @@ -1,13 +1,14 @@ @@ -16,25 +17,21 @@ - {#each variants as variant} - {#each [false, true] as outline} {#each sizes as size} {#each icons as {iconPosition, icon}} - - + {/each} {/each} {/each} - {/each} -
VariantOutlined Size Icon Position
{variant}{outline} {size} {iconPosition}
\ No newline at end of file +