@svgrid/enterprise 3.1.0 → 3.1.2
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/dist/SvSheet.svelte +93 -8
- package/dist/cdn/svgrid-enterprise.svelte-external.js +9339 -9077
- package/dist/designer/assets/{GridMenus-8gTNssgV.js → GridMenus-DGZJKFvX.js} +1 -1
- package/dist/designer/assets/SvGrid.helpers-DE5mmORW.js +1 -0
- package/dist/designer/assets/{SvGridCellEditor-DMoWJjEI.js → SvGridCellEditor-C7NWcnNC.js} +1 -1
- package/dist/designer/assets/{SvGridChart-Cy1ATrWh.js → SvGridChart-Fg43Yi0M.js} +2 -2
- package/dist/designer/assets/{SvGridChartBuilder-eQ3mn0L9.js → SvGridChartBuilder-BqGuA3yU.js} +1 -1
- package/dist/designer/assets/{SvGridChartPanel-CATdVc9X.js → SvGridChartPanel-BsiXRAnx.js} +3 -3
- package/dist/designer/assets/{SvGridChartView-CmsZtCED.js → SvGridChartView-BgttK0tR.js} +1 -1
- package/dist/designer/assets/{SvListBox-BDS66lmZ.js → SvListBox-v2jm0-vf.js} +1 -1
- package/dist/designer/assets/cell-formatting-BWrW_O0n.js +1 -0
- package/dist/designer/assets/{chart-Cn7MnNqA.js → chart-BIl-dIRv.js} +1 -1
- package/dist/designer/assets/{chart-panel-messages-Dz7PTIqT.js → chart-panel-messages-4cF2vw7L.js} +1 -1
- package/dist/designer/assets/{chart-summary-BD1qYB2R.js → chart-summary-B7bTtSCC.js} +1 -1
- package/dist/designer/assets/{chart-trend-B8xr5LuD.js → chart-trend-CLsn5Hgd.js} +1 -1
- package/dist/designer/assets/export-format-DGscOjS9.js +6 -0
- package/dist/designer/assets/{index-BSjv6Qec.js → index-V_cMXdxt.js} +314 -314
- package/dist/designer/assets/index-l21iGkpF.css +1 -0
- package/dist/designer/assets/src-Mu7AGqT8.js +58 -0
- package/dist/designer/assets/validate-YxSrBF1H.js +1 -0
- package/dist/designer/index.html +12 -12
- package/dist/export-ooxml.d.ts +10 -0
- package/dist/export-ooxml.js +22 -5
- package/dist/import.js +11 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/node/studio.js +236 -132
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/server-row-model.d.ts +9 -0
- package/dist/server/server-row-model.js +120 -3
- package/dist/server/worker-data-source.d.ts +62 -0
- package/dist/server/worker-data-source.js +133 -0
- package/dist/sheet/objects.d.ts +47 -0
- package/dist/sheet/objects.js +33 -0
- package/dist/studio/cli.js +55 -1
- package/dist/studio/emit-project.js +8 -3
- package/dist/studio/init-flow.d.ts +6 -1
- package/dist/studio/init-flow.js +43 -2
- package/dist/studio/scaffold-app.d.ts +6 -0
- package/dist/studio/scaffold-app.js +5 -3
- package/dist/studio/scaffold.js +29 -12
- package/dist/sveltekit/in-memory.d.ts +21 -1
- package/dist/sveltekit/in-memory.js +153 -105
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/version.d.ts +14 -1
- package/dist/version.js +14 -1
- package/dist/wc/{GridMenus-By9vPuq0.js → GridMenus-6_fF5jIm.js} +2 -2
- package/dist/wc/{SvGrid.helpers-BdVDtjUm.js → SvGrid.helpers-zBkDTryR.js} +36 -37
- package/dist/wc/{SvGridCellEditor-Bnf1Kr5C.js → SvGridCellEditor-CzUKEbkI.js} +1 -1
- package/dist/wc/{SvGridChart-BqUJh2pG.js → SvGridChart-C6UvNIbN.js} +3 -3
- package/dist/wc/{SvGridChartBuilder-CGycC5cn.js → SvGridChartBuilder-ikf_5LyW.js} +3 -3
- package/dist/wc/{SvGridChartPanel-CRnizsHD.js → SvGridChartPanel-Dc_jj7B-.js} +4 -4
- package/dist/wc/{SvGridChartView-D1pa_xMy.js → SvGridChartView-thkznAOb.js} +2 -2
- package/dist/wc/{cell-formatting-CBfgahv1.js → cell-formatting-hb3vzxhJ.js} +27 -5
- package/dist/wc/{chart-6OXlybVT.js → chart-CV5D7_k8.js} +1 -1
- package/dist/wc/{chart-summary-2m3ia1-n.js → chart-summary-DGx0QhJa.js} +1 -1
- package/dist/wc/{chart-trend-CQOQtTMl.js → chart-trend-BIRH_xH8.js} +1 -1
- package/dist/wc/sv-sheet-element.js +7454 -7352
- package/dist/wc/{svelte-virtualizer.svelte-CXtQy2TQ.js → svelte-virtualizer.svelte-Du4tjrIP.js} +7 -3
- package/dist/wc/{validate-BqDthkNr.js → validate-JAuMsk6Z.js} +36 -36
- package/dist/worker/columnar.d.ts +42 -0
- package/dist/worker/columnar.js +410 -0
- package/dist/worker/index.d.ts +12 -0
- package/dist/worker/index.js +12 -0
- package/dist/worker/infer-schema.d.ts +21 -0
- package/dist/worker/infer-schema.js +34 -0
- package/dist/worker/protocol.d.ts +73 -0
- package/dist/worker/protocol.js +14 -0
- package/dist/worker/serve.d.ts +44 -0
- package/dist/worker/serve.js +100 -0
- package/package.json +7 -3
- package/dist/designer/assets/SvGrid.helpers-BgT-gnCo.js +0 -1
- package/dist/designer/assets/cell-formatting-BbubJTVz.js +0 -1
- package/dist/designer/assets/export-format-DGckmgKr.js +0 -6
- package/dist/designer/assets/index-_JVFxt02.css +0 -1
- package/dist/designer/assets/src-DUQ4xE3x.js +0 -58
- package/dist/designer/assets/validate-C3B8QKsM.js +0 -1
package/dist/node/studio.js
CHANGED
|
@@ -1721,7 +1721,8 @@ ${managed$1(serverBody)}
|
|
|
1721
1721
|
const pageBody = ` const source = createKitDataSource<${rowType}>({ endpoint: '${apiRoute}' })
|
|
1722
1722
|
const columns = schemaToColumns(${constName})${hasLookups ? `\n\n${lookupDecls}\n${lookupsMap}` : ""}
|
|
1723
1723
|
|
|
1724
|
-
|
|
1724
|
+
// Not "state": a local of that name makes svelte-check read $state as a store of it.
|
|
1725
|
+
let view = $state<ServerState<${rowType}>>({
|
|
1725
1726
|
rows: [], total: 0, loading: false, saving: false, error: null,
|
|
1726
1727
|
pageIndex: 0, pageSize: 25, pageCount: 1, sortModel: [], filterModel: {},
|
|
1727
1728
|
})
|
|
@@ -1729,7 +1730,7 @@ ${managed$1(serverBody)}
|
|
|
1729
1730
|
pageSize: 25,
|
|
1730
1731
|
optimistic: true,
|
|
1731
1732
|
getRowId: (r) => String(r.${idField}),
|
|
1732
|
-
onChange: (s) => (
|
|
1733
|
+
onChange: (s) => (view = s),
|
|
1733
1734
|
})
|
|
1734
1735
|
controller.refresh()
|
|
1735
1736
|
|
|
@@ -1739,7 +1740,7 @@ ${managed$1(serverBody)}
|
|
|
1739
1740
|
async function save({ mode, id, values }: { mode: 'create' | 'edit'; id: string | null; values: Partial<${rowType}> }) {
|
|
1740
1741
|
if (mode === 'create') {
|
|
1741
1742
|
await controller.createRow(values)
|
|
1742
|
-
controller.setPage(
|
|
1743
|
+
controller.setPage(view.pageCount - 1) // jump to the new row (appended at the end)
|
|
1743
1744
|
} else if (id) {
|
|
1744
1745
|
await controller.updateRow(id, values)
|
|
1745
1746
|
}
|
|
@@ -1757,12 +1758,11 @@ ${managed$1(serverBody)}
|
|
|
1757
1758
|
path: `src/routes/${route}/+page.svelte`,
|
|
1758
1759
|
description: "The data screen: sort, filter, search, paging, multi-select delete, and create/edit - wired to the API route.",
|
|
1759
1760
|
contents: `<script lang="ts">
|
|
1760
|
-
import { SvGrid, createServerDataSource } from '@svgrid/grid'
|
|
1761
|
+
import { SvGrid, createServerDataSource, type ServerState } from '@svgrid/grid'
|
|
1761
1762
|
import {
|
|
1762
1763
|
SvGridEditPanel,
|
|
1763
1764
|
createKitDataSource,${hasLookups ? "\n createRelationLookup," : ""}
|
|
1764
1765
|
schemaToColumns,
|
|
1765
|
-
type ServerState,
|
|
1766
1766
|
} from '@svgrid/enterprise'
|
|
1767
1767
|
import { ${constName}, type ${rowType} } from '${schemaImport}'
|
|
1768
1768
|
|
|
@@ -1787,12 +1787,12 @@ ${themeTokenCss({
|
|
|
1787
1787
|
</div>
|
|
1788
1788
|
</header>
|
|
1789
1789
|
|
|
1790
|
-
{#if
|
|
1790
|
+
{#if view.error}<p class="page__error" role="alert">{view.error}</p>{/if}
|
|
1791
1791
|
|
|
1792
1792
|
<SvGrid
|
|
1793
|
-
data={
|
|
1793
|
+
data={view.rows}
|
|
1794
1794
|
{columns}
|
|
1795
|
-
loading={
|
|
1795
|
+
loading={view.loading}
|
|
1796
1796
|
loadingOverlay
|
|
1797
1797
|
fitColumns
|
|
1798
1798
|
enableRowSummaries={false}
|
|
@@ -1815,11 +1815,11 @@ ${themeTokenCss({
|
|
|
1815
1815
|
onRowSelectionChange={(_sel, rows) => (selected = rows)}
|
|
1816
1816
|
showPagination
|
|
1817
1817
|
externalPagination
|
|
1818
|
-
rowCount={
|
|
1819
|
-
pageIndex={
|
|
1820
|
-
pageSize={
|
|
1818
|
+
rowCount={view.total}
|
|
1819
|
+
pageIndex={view.pageIndex}
|
|
1820
|
+
pageSize={view.pageSize}
|
|
1821
1821
|
onPaginationChange={({ pageIndex, pageSize }) => {
|
|
1822
|
-
if (pageSize !==
|
|
1822
|
+
if (pageSize !== view.pageSize) controller.setPageSize(pageSize)
|
|
1823
1823
|
else controller.setPage(pageIndex)
|
|
1824
1824
|
}}
|
|
1825
1825
|
emptyMessage="No ${label} yet"
|
|
@@ -1851,6 +1851,7 @@ ${themeTokenCss({
|
|
|
1851
1851
|
*/
|
|
1852
1852
|
function mergeManaged(existing, generated) {
|
|
1853
1853
|
if (!existing) return generated;
|
|
1854
|
+
existing = migrateStateLocal(existing);
|
|
1854
1855
|
const startIdx = existing.indexOf(MANAGED_START);
|
|
1855
1856
|
const endIdx = existing.indexOf(MANAGED_END);
|
|
1856
1857
|
if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) return generated;
|
|
@@ -1860,6 +1861,21 @@ function mergeManaged(existing, generated) {
|
|
|
1860
1861
|
const newRegion = generated.slice(genStart, genEnd + 21);
|
|
1861
1862
|
return existing.slice(0, startIdx) + newRegion + existing.slice(endIdx + 21);
|
|
1862
1863
|
}
|
|
1864
|
+
/** The managed line pages scaffolded before the rename carried. */
|
|
1865
|
+
var OLD_STATE_LOCAL = /\blet state = \$state<ServerState</;
|
|
1866
|
+
/**
|
|
1867
|
+
* Pages scaffolded before the rename kept their server state in a local named
|
|
1868
|
+
* `state`, which svelte-check reads as a store subscription whenever `$state`
|
|
1869
|
+
* appears. The managed region now calls it `view`, but the markup below the
|
|
1870
|
+
* region is the user's and still says `state.rows`; a plain merge would leave it
|
|
1871
|
+
* pointing at nothing. Rename the references too. Nothing else in such a page
|
|
1872
|
+
* could be called `state` (the old local would have clashed), and member reads
|
|
1873
|
+
* like `page.state` are skipped.
|
|
1874
|
+
*/
|
|
1875
|
+
function migrateStateLocal(existing) {
|
|
1876
|
+
if (!OLD_STATE_LOCAL.test(existing)) return existing;
|
|
1877
|
+
return existing.replace(/(?<![.\w$])state(?=\s*[.?[]|\s*=[^=>])/g, "view");
|
|
1878
|
+
}
|
|
1863
1879
|
//#endregion
|
|
1864
1880
|
//#region src/studio/scaffold-app.ts
|
|
1865
1881
|
/**
|
|
@@ -1875,12 +1891,12 @@ function mergeManaged(existing, generated) {
|
|
|
1875
1891
|
*/
|
|
1876
1892
|
var MANAGED_NOTE = "// Regenerated by SvGrid Studio. Edits inside these markers are overwritten.";
|
|
1877
1893
|
var managed = (body) => `${MANAGED_START}\n${MANAGED_NOTE}\n${body}\n${MANAGED_END}`;
|
|
1878
|
-
function layoutFile$1(nav, appTitle, theme) {
|
|
1894
|
+
function layoutFile$1(nav, appTitle, theme, stylesheets) {
|
|
1879
1895
|
return {
|
|
1880
1896
|
path: "src/routes/+layout.svelte",
|
|
1881
1897
|
description: "App shell: a collapsible nav sidebar linking every entity screen (an off-canvas drawer on tablet/phone), with the routed page in the main area.",
|
|
1882
1898
|
contents: `<script lang="ts">
|
|
1883
|
-
${managed(` let { children } = $props()
|
|
1899
|
+
${managed(`${stylesheets.map((css) => ` import '../${css}'\n`).join("")} let { children } = $props()
|
|
1884
1900
|
const appTitle = ${JSON.stringify(appTitle)}
|
|
1885
1901
|
const nav = ${JSON.stringify(nav)}
|
|
1886
1902
|
|
|
@@ -2003,10 +2019,11 @@ function scaffoldApp(schemas, options = {}) {
|
|
|
2003
2019
|
href: `/${s.name}`,
|
|
2004
2020
|
label: s.label ?? titleCase$2(s.name)
|
|
2005
2021
|
}));
|
|
2006
|
-
|
|
2022
|
+
const theme = {
|
|
2007
2023
|
preset: options.theme,
|
|
2008
2024
|
mode: options.dark ? "dark" : "light"
|
|
2009
|
-
}
|
|
2025
|
+
};
|
|
2026
|
+
files.push(layoutFile$1(nav, appTitle, theme, options.stylesheets ?? []), homeFile$1(nav, appTitle));
|
|
2010
2027
|
return { files };
|
|
2011
2028
|
}
|
|
2012
2029
|
//#endregion
|
|
@@ -16028,7 +16045,20 @@ function hasFieldConditions(schema) {
|
|
|
16028
16045
|
*
|
|
16029
16046
|
* `version.test.ts` asserts this matches `package.json`, so the two cannot drift.
|
|
16030
16047
|
*/
|
|
16031
|
-
var SVGRID_VERSION = "3.1.
|
|
16048
|
+
var SVGRID_VERSION = "3.1.2";
|
|
16049
|
+
/**
|
|
16050
|
+
* The version of `@svgrid/grid`, which releases on its own line: enterprise is
|
|
16051
|
+
* on 3.1.x while the grid is on 3.0.x.
|
|
16052
|
+
*
|
|
16053
|
+
* A scaffolded app depends on BOTH packages, and pinning the grid to this
|
|
16054
|
+
* package's version asked npm for `@svgrid/grid@^3.1.1`, which has never been
|
|
16055
|
+
* published. Every `svgrid-studio init` therefore produced an app whose very
|
|
16056
|
+
* next step, `npm install`, failed with ERESOLVE. Found 2026-10-05 by
|
|
16057
|
+
* scaffolding one and running the install.
|
|
16058
|
+
*
|
|
16059
|
+
* `version.test.ts` asserts this matches packages/grid/package.json.
|
|
16060
|
+
*/
|
|
16061
|
+
var GRID_VERSION = "3.0.8";
|
|
16032
16062
|
//#endregion
|
|
16033
16063
|
//#region src/studio/emit-project.ts
|
|
16034
16064
|
var has = (blocks, kind) => blocks.some((b) => b.config.kind === kind);
|
|
@@ -20885,10 +20915,9 @@ function svelteConfig(plan) {
|
|
|
20885
20915
|
/** The runtime npm deps the generated source imports, keyed by package -> range.
|
|
20886
20916
|
* One source of truth for package.json AND the fragment-mode dependency report. */
|
|
20887
20917
|
function runtimeDeps(project, allSource) {
|
|
20888
|
-
const svgrid = `^${SVGRID_VERSION}`;
|
|
20889
20918
|
const dependencies = {
|
|
20890
|
-
"@svgrid/grid":
|
|
20891
|
-
"@svgrid/enterprise":
|
|
20919
|
+
"@svgrid/grid": `^${GRID_VERSION}`,
|
|
20920
|
+
"@svgrid/enterprise": `^${SVGRID_VERSION}`
|
|
20892
20921
|
};
|
|
20893
20922
|
if (allSource.includes("from '@supabase/supabase-js'")) dependencies["@supabase/supabase-js"] = "^2.45.0";
|
|
20894
20923
|
if (allSource.includes("import pg from 'pg'")) dependencies["pg"] = "^8.11.0";
|
|
@@ -40702,6 +40731,153 @@ function getStarterDataset(id) {
|
|
|
40702
40731
|
return starterDatasets.find((d) => d.id === id);
|
|
40703
40732
|
}
|
|
40704
40733
|
//#endregion
|
|
40734
|
+
//#region src/studio/verify.ts
|
|
40735
|
+
async function verifyScaffold(files) {
|
|
40736
|
+
const issues = [];
|
|
40737
|
+
let checked = 0;
|
|
40738
|
+
let skipped = 0;
|
|
40739
|
+
let compile;
|
|
40740
|
+
try {
|
|
40741
|
+
compile = (await import("svelte/compiler")).compile;
|
|
40742
|
+
} catch {
|
|
40743
|
+
compile = void 0;
|
|
40744
|
+
}
|
|
40745
|
+
for (const file of files) {
|
|
40746
|
+
if (!file.path.endsWith(".svelte") || !compile) {
|
|
40747
|
+
skipped++;
|
|
40748
|
+
continue;
|
|
40749
|
+
}
|
|
40750
|
+
try {
|
|
40751
|
+
const { warnings } = compile(file.contents, {
|
|
40752
|
+
filename: file.path,
|
|
40753
|
+
generate: "client"
|
|
40754
|
+
});
|
|
40755
|
+
checked++;
|
|
40756
|
+
for (const w of warnings) issues.push({
|
|
40757
|
+
path: file.path,
|
|
40758
|
+
severity: "warning",
|
|
40759
|
+
message: w.message ?? "warning"
|
|
40760
|
+
});
|
|
40761
|
+
} catch (err) {
|
|
40762
|
+
checked++;
|
|
40763
|
+
issues.push({
|
|
40764
|
+
path: file.path,
|
|
40765
|
+
severity: "error",
|
|
40766
|
+
message: err instanceof Error ? err.message : String(err)
|
|
40767
|
+
});
|
|
40768
|
+
}
|
|
40769
|
+
}
|
|
40770
|
+
return {
|
|
40771
|
+
ok: !issues.some((i) => i.severity === "error"),
|
|
40772
|
+
issues,
|
|
40773
|
+
checked,
|
|
40774
|
+
skipped
|
|
40775
|
+
};
|
|
40776
|
+
}
|
|
40777
|
+
/** One-line human summary of a verify result, for tool output / logs. */
|
|
40778
|
+
function summarizeVerify(result) {
|
|
40779
|
+
if (result.checked === 0) return "verification skipped (Svelte compiler unavailable) - run svelte-check in your project.";
|
|
40780
|
+
const errors = result.issues.filter((i) => i.severity === "error");
|
|
40781
|
+
if (errors.length === 0) return `verified: ${result.checked} file(s) compile.`;
|
|
40782
|
+
return `VERIFICATION FAILED (${errors.length} error(s)):\n` + errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
|
|
40783
|
+
}
|
|
40784
|
+
//#endregion
|
|
40785
|
+
//#region src/studio/cli.ts
|
|
40786
|
+
/** A `.prisma` file (by extension or a `model X {` block) parses as Prisma; otherwise Drizzle. */
|
|
40787
|
+
function isPrismaSource(path, source) {
|
|
40788
|
+
return /\.prisma$/i.test(path) || /\bmodel\s+\w+\s*\{/.test(source);
|
|
40789
|
+
}
|
|
40790
|
+
/** Resolve an EntitySchema from whichever input was provided. */
|
|
40791
|
+
async function resolveSchema(options, io) {
|
|
40792
|
+
if (options.schema) return options.schema;
|
|
40793
|
+
if (options.from) {
|
|
40794
|
+
const src = await io.readFile(options.from);
|
|
40795
|
+
if (src == null) throw new Error(`runStudioAdd: schema file not found: ${options.from}`);
|
|
40796
|
+
return isPrismaSource(options.from, src) ? introspectPrisma(src, options.table) : introspectDrizzle(src, options.table);
|
|
40797
|
+
}
|
|
40798
|
+
if (options.rows) return introspectJson(options.name ?? options.route ?? "entity", options.rows);
|
|
40799
|
+
throw new Error("runStudioAdd: provide `from` (a Drizzle or Prisma schema file), `rows`, or `schema`");
|
|
40800
|
+
}
|
|
40801
|
+
/**
|
|
40802
|
+
* Resolve **every** entity from a Drizzle or Prisma schema file (the `--all
|
|
40803
|
+
* --from` path), linked so foreign keys become working relation lookups.
|
|
40804
|
+
*/
|
|
40805
|
+
async function resolveSchemas(from, io) {
|
|
40806
|
+
const src = await io.readFile(from);
|
|
40807
|
+
if (src == null) throw new Error(`runStudioAddApp: schema file not found: ${from}`);
|
|
40808
|
+
return isPrismaSource(from, src) ? introspectPrismaAll(src) : introspectDrizzleAll(src);
|
|
40809
|
+
}
|
|
40810
|
+
/** Merge-write a set of generated files, preserving user edits outside managed regions. */
|
|
40811
|
+
async function writeAll$1(files, io) {
|
|
40812
|
+
const written = [];
|
|
40813
|
+
for (const file of files) {
|
|
40814
|
+
const existing = await io.readFile(file.path);
|
|
40815
|
+
if (skipUserOwned(file, existing != null)) continue;
|
|
40816
|
+
await io.writeFile(file.path, mergeManaged(existing, file.contents));
|
|
40817
|
+
written.push(file.path);
|
|
40818
|
+
}
|
|
40819
|
+
return written;
|
|
40820
|
+
}
|
|
40821
|
+
async function runStudioAdd(options, io) {
|
|
40822
|
+
const schema = await resolveSchema(options, io);
|
|
40823
|
+
const { files } = scaffold(schema, options);
|
|
40824
|
+
return {
|
|
40825
|
+
schema,
|
|
40826
|
+
written: await writeAll$1(files, io),
|
|
40827
|
+
verify: await verifyScaffold(files)
|
|
40828
|
+
};
|
|
40829
|
+
}
|
|
40830
|
+
/** The stylesheets an `init`-generated layout imports, in cascade order. */
|
|
40831
|
+
var APP_STYLESHEETS = ["app.css", "custom.css"];
|
|
40832
|
+
/**
|
|
40833
|
+
* Fill the app-shell options the caller left out from the app already on disk,
|
|
40834
|
+
* so `add --all` in a folder `init` generated keeps its stylesheets, title and
|
|
40835
|
+
* theme. The shell layout from `init` carries no managed markers, so the merge
|
|
40836
|
+
* write replaces it whole; anything it imported or named has to be carried
|
|
40837
|
+
* over here or it is lost.
|
|
40838
|
+
*
|
|
40839
|
+
* Title: studio.config.json (shell brand, then title), else the `appTitle`
|
|
40840
|
+
* constant of a layout an earlier `add --all` wrote.
|
|
40841
|
+
*/
|
|
40842
|
+
async function appShellDefaults(options, io) {
|
|
40843
|
+
const stylesheets = options.stylesheets ?? [];
|
|
40844
|
+
if (!options.stylesheets) {
|
|
40845
|
+
for (const css of APP_STYLESHEETS) if (await io.readFile(`src/${css}`) != null) stylesheets.push(css);
|
|
40846
|
+
}
|
|
40847
|
+
let title;
|
|
40848
|
+
let theme;
|
|
40849
|
+
const config = await io.readFile("studio.config.json");
|
|
40850
|
+
if (config != null) try {
|
|
40851
|
+
const parsed = JSON.parse(config);
|
|
40852
|
+
theme = parsed.theme;
|
|
40853
|
+
title = (typeof theme?.shell?.brand === "string" ? theme.shell.brand.trim() : "") || (typeof parsed.title === "string" ? parsed.title : void 0);
|
|
40854
|
+
} catch {}
|
|
40855
|
+
if (!title) {
|
|
40856
|
+
const m = (await io.readFile("src/routes/+layout.svelte"))?.match(/const appTitle = ("(?:[^"\\]|\\.)*")/);
|
|
40857
|
+
if (m) try {
|
|
40858
|
+
title = JSON.parse(m[1]);
|
|
40859
|
+
} catch {}
|
|
40860
|
+
}
|
|
40861
|
+
return {
|
|
40862
|
+
...options,
|
|
40863
|
+
stylesheets,
|
|
40864
|
+
...options.appTitle == null && title ? { appTitle: title } : {},
|
|
40865
|
+
...options.theme == null && typeof theme?.preset === "string" ? { theme: theme.preset } : {},
|
|
40866
|
+
...options.dark == null && theme?.mode === "dark" ? { dark: true } : {}
|
|
40867
|
+
};
|
|
40868
|
+
}
|
|
40869
|
+
/**
|
|
40870
|
+
* Scaffold a whole multi-entity app - every entity's screen plus a nav layout
|
|
40871
|
+
* and home page - and merge-write it. The `--all` path behind `svgrid-studio`.
|
|
40872
|
+
*/
|
|
40873
|
+
async function runStudioAddApp(schemas, options, io) {
|
|
40874
|
+
const { files } = scaffoldApp(schemas, await appShellDefaults(options, io));
|
|
40875
|
+
return {
|
|
40876
|
+
written: await writeAll$1(files, io),
|
|
40877
|
+
verify: await verifyScaffold(files)
|
|
40878
|
+
};
|
|
40879
|
+
}
|
|
40880
|
+
//#endregion
|
|
40705
40881
|
//#region src/sources/introspect-supabase.ts
|
|
40706
40882
|
var mapOpenApiType = (prop) => {
|
|
40707
40883
|
if (Array.isArray(prop.enum)) return "enum";
|
|
@@ -40966,7 +41142,8 @@ function unwrapRows(body) {
|
|
|
40966
41142
|
return [];
|
|
40967
41143
|
}
|
|
40968
41144
|
/** Step 1-2: where does the data come from, and what tables does it have? */
|
|
40969
|
-
async function gatherData(flags, prompts, db, fetchText) {
|
|
41145
|
+
async function gatherData(flags, prompts, db, fetchText, io) {
|
|
41146
|
+
if (flags.from) return gatherFromSchemaFile(flags, flags.from, prompts, io);
|
|
40970
41147
|
let source = "sample";
|
|
40971
41148
|
if (flags.db) source = "db";
|
|
40972
41149
|
else if (flags.supabaseUrl) source = "supabase";
|
|
@@ -40998,6 +41175,41 @@ async function gatherData(flags, prompts, db, fetchText) {
|
|
|
40998
41175
|
title: dataset.name
|
|
40999
41176
|
};
|
|
41000
41177
|
}
|
|
41178
|
+
/**
|
|
41179
|
+
* A schema file names the tables and columns but holds no rows and no
|
|
41180
|
+
* connection, so the app runs on the in-memory source and the emitter seeds it
|
|
41181
|
+
* with sample rows. `--db` + `--url` is the path to a live database.
|
|
41182
|
+
*/
|
|
41183
|
+
async function gatherFromSchemaFile(flags, from, prompts, io) {
|
|
41184
|
+
const clash = flags.db ? "--db" : flags.supabaseUrl ? "--supabase-url" : flags.dataset ? "--dataset" : null;
|
|
41185
|
+
if (clash) throw new UserError(`--from and ${clash} both name the data source.`, "Pass one of them.");
|
|
41186
|
+
if (await io.readFile(from) == null) throw new UserError(`Schema file not found: ${from}`, "Pass the path to a Drizzle schema (.ts) or a Prisma schema (.prisma).");
|
|
41187
|
+
let all;
|
|
41188
|
+
try {
|
|
41189
|
+
all = await resolveSchemas(from, io);
|
|
41190
|
+
} catch (e) {
|
|
41191
|
+
throw new UserError(`Could not read ${from}: ${e instanceof Error ? e.message : String(e)}`, "Pass a Drizzle schema (.ts) or a Prisma schema (.prisma).");
|
|
41192
|
+
}
|
|
41193
|
+
if (all.length === 0) throw new UserError(`No tables or models found in ${from}.`, "Drizzle tables are read from pgTable / mysqlTable / sqliteTable calls, Prisma models from `model X {` blocks.");
|
|
41194
|
+
let picked = all.map((s) => s.name);
|
|
41195
|
+
if (!flags.yes) {
|
|
41196
|
+
prompts.say("");
|
|
41197
|
+
prompts.say(`Found ${all.length} ${all.length === 1 ? "table" : "tables"} in ${from}:`);
|
|
41198
|
+
all.forEach((s, i) => prompts.say(` ${i + 1}. ${s.name}`));
|
|
41199
|
+
picked = parseSelection(await prompts.ask("Which tables? (all, or a comma list of names/numbers)", "all"), picked);
|
|
41200
|
+
}
|
|
41201
|
+
const schemas = all.filter((s) => picked.includes(s.name));
|
|
41202
|
+
if (schemas.length === 0) throw new UserError("No tables picked - nothing to build.");
|
|
41203
|
+
const sources = {};
|
|
41204
|
+
for (const s of schemas) sources[s.name] = { kind: "memory" };
|
|
41205
|
+
prompts.say(` Read ${schemas.length} ${schemas.length === 1 ? "table" : "tables"} from ${from} (in-memory data with sample rows).`);
|
|
41206
|
+
return {
|
|
41207
|
+
schemas,
|
|
41208
|
+
sources,
|
|
41209
|
+
seed: {},
|
|
41210
|
+
kind: "memory"
|
|
41211
|
+
};
|
|
41212
|
+
}
|
|
41001
41213
|
async function gatherFromDatabase(flags, prompts, db) {
|
|
41002
41214
|
if (!db) throw new UserError("Connecting to a database is not available here.");
|
|
41003
41215
|
let dialect = flags.db;
|
|
@@ -41263,7 +41475,7 @@ async function confirmTargetFolder(outDir, flags, prompts, io, before) {
|
|
|
41263
41475
|
if (!isYes(await prompts.ask("Overwrite? (y/N)", "n"), false)) throw new UserError("Stopped without writing anything.", "Re-run with --out <dir> to build somewhere else.");
|
|
41264
41476
|
}
|
|
41265
41477
|
/** Merge-write the generated files, preserving edits outside managed regions. */
|
|
41266
|
-
async function writeAll
|
|
41478
|
+
async function writeAll(files, outDir, io) {
|
|
41267
41479
|
const written = [];
|
|
41268
41480
|
for (const file of files) {
|
|
41269
41481
|
const path = inDir(outDir, file.path);
|
|
@@ -41282,7 +41494,7 @@ async function runStudioInit(flags, prompts, db, io, fetchText = null) {
|
|
|
41282
41494
|
prompts.say("SvGrid Studio - let's build your app.");
|
|
41283
41495
|
const candidateDir = flags.out ?? ".";
|
|
41284
41496
|
const markersBefore = await markersIn(candidateDir, io);
|
|
41285
|
-
const data = await gatherData(flags, prompts, db, fetchText);
|
|
41497
|
+
const data = await gatherData(flags, prompts, db, fetchText, io);
|
|
41286
41498
|
const perEntity = await askScreens(data.schemas, flags, prompts);
|
|
41287
41499
|
const theme = await askTheme(flags, prompts);
|
|
41288
41500
|
const defaultTitle = flags.title ?? data.title ?? "My app";
|
|
@@ -41298,7 +41510,7 @@ async function runStudioInit(flags, prompts, db, io, fetchText = null) {
|
|
|
41298
41510
|
overviewDashboard: true,
|
|
41299
41511
|
...theme ? { theme } : {}
|
|
41300
41512
|
});
|
|
41301
|
-
const written = await writeAll
|
|
41513
|
+
const written = await writeAll(emitStudioAppBundle(project), outDir, io);
|
|
41302
41514
|
const configPath = inDir(outDir, "studio.config.json");
|
|
41303
41515
|
await io.writeFile(configPath, serializeProject(project));
|
|
41304
41516
|
written.push(configPath);
|
|
@@ -41466,57 +41678,6 @@ function buildStudioBugReport(input) {
|
|
|
41466
41678
|
};
|
|
41467
41679
|
}
|
|
41468
41680
|
//#endregion
|
|
41469
|
-
//#region src/studio/verify.ts
|
|
41470
|
-
async function verifyScaffold(files) {
|
|
41471
|
-
const issues = [];
|
|
41472
|
-
let checked = 0;
|
|
41473
|
-
let skipped = 0;
|
|
41474
|
-
let compile;
|
|
41475
|
-
try {
|
|
41476
|
-
compile = (await import("svelte/compiler")).compile;
|
|
41477
|
-
} catch {
|
|
41478
|
-
compile = void 0;
|
|
41479
|
-
}
|
|
41480
|
-
for (const file of files) {
|
|
41481
|
-
if (!file.path.endsWith(".svelte") || !compile) {
|
|
41482
|
-
skipped++;
|
|
41483
|
-
continue;
|
|
41484
|
-
}
|
|
41485
|
-
try {
|
|
41486
|
-
const { warnings } = compile(file.contents, {
|
|
41487
|
-
filename: file.path,
|
|
41488
|
-
generate: "client"
|
|
41489
|
-
});
|
|
41490
|
-
checked++;
|
|
41491
|
-
for (const w of warnings) issues.push({
|
|
41492
|
-
path: file.path,
|
|
41493
|
-
severity: "warning",
|
|
41494
|
-
message: w.message ?? "warning"
|
|
41495
|
-
});
|
|
41496
|
-
} catch (err) {
|
|
41497
|
-
checked++;
|
|
41498
|
-
issues.push({
|
|
41499
|
-
path: file.path,
|
|
41500
|
-
severity: "error",
|
|
41501
|
-
message: err instanceof Error ? err.message : String(err)
|
|
41502
|
-
});
|
|
41503
|
-
}
|
|
41504
|
-
}
|
|
41505
|
-
return {
|
|
41506
|
-
ok: !issues.some((i) => i.severity === "error"),
|
|
41507
|
-
issues,
|
|
41508
|
-
checked,
|
|
41509
|
-
skipped
|
|
41510
|
-
};
|
|
41511
|
-
}
|
|
41512
|
-
/** One-line human summary of a verify result, for tool output / logs. */
|
|
41513
|
-
function summarizeVerify(result) {
|
|
41514
|
-
if (result.checked === 0) return "verification skipped (Svelte compiler unavailable) - run svelte-check in your project.";
|
|
41515
|
-
const errors = result.issues.filter((i) => i.severity === "error");
|
|
41516
|
-
if (errors.length === 0) return `verified: ${result.checked} file(s) compile.`;
|
|
41517
|
-
return `VERIFICATION FAILED (${errors.length} error(s)):\n` + errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
|
|
41518
|
-
}
|
|
41519
|
-
//#endregion
|
|
41520
41681
|
//#region src/studio/copilot-core.ts
|
|
41521
41682
|
/**
|
|
41522
41683
|
* Studio Copilot - the pure, testable core. Builds the model request for "edit
|
|
@@ -41660,63 +41821,6 @@ function missingEnvKeys(envExample, envLocal) {
|
|
|
41660
41821
|
return declared.filter((k) => !present.has(k));
|
|
41661
41822
|
}
|
|
41662
41823
|
//#endregion
|
|
41663
|
-
//#region src/studio/cli.ts
|
|
41664
|
-
/** A `.prisma` file (by extension or a `model X {` block) parses as Prisma; otherwise Drizzle. */
|
|
41665
|
-
function isPrismaSource(path, source) {
|
|
41666
|
-
return /\.prisma$/i.test(path) || /\bmodel\s+\w+\s*\{/.test(source);
|
|
41667
|
-
}
|
|
41668
|
-
/** Resolve an EntitySchema from whichever input was provided. */
|
|
41669
|
-
async function resolveSchema(options, io) {
|
|
41670
|
-
if (options.schema) return options.schema;
|
|
41671
|
-
if (options.from) {
|
|
41672
|
-
const src = await io.readFile(options.from);
|
|
41673
|
-
if (src == null) throw new Error(`runStudioAdd: schema file not found: ${options.from}`);
|
|
41674
|
-
return isPrismaSource(options.from, src) ? introspectPrisma(src, options.table) : introspectDrizzle(src, options.table);
|
|
41675
|
-
}
|
|
41676
|
-
if (options.rows) return introspectJson(options.name ?? options.route ?? "entity", options.rows);
|
|
41677
|
-
throw new Error("runStudioAdd: provide `from` (a Drizzle or Prisma schema file), `rows`, or `schema`");
|
|
41678
|
-
}
|
|
41679
|
-
/**
|
|
41680
|
-
* Resolve **every** entity from a Drizzle or Prisma schema file (the `--all
|
|
41681
|
-
* --from` path), linked so foreign keys become working relation lookups.
|
|
41682
|
-
*/
|
|
41683
|
-
async function resolveSchemas(from, io) {
|
|
41684
|
-
const src = await io.readFile(from);
|
|
41685
|
-
if (src == null) throw new Error(`runStudioAddApp: schema file not found: ${from}`);
|
|
41686
|
-
return isPrismaSource(from, src) ? introspectPrismaAll(src) : introspectDrizzleAll(src);
|
|
41687
|
-
}
|
|
41688
|
-
/** Merge-write a set of generated files, preserving user edits outside managed regions. */
|
|
41689
|
-
async function writeAll(files, io) {
|
|
41690
|
-
const written = [];
|
|
41691
|
-
for (const file of files) {
|
|
41692
|
-
const existing = await io.readFile(file.path);
|
|
41693
|
-
if (skipUserOwned(file, existing != null)) continue;
|
|
41694
|
-
await io.writeFile(file.path, mergeManaged(existing, file.contents));
|
|
41695
|
-
written.push(file.path);
|
|
41696
|
-
}
|
|
41697
|
-
return written;
|
|
41698
|
-
}
|
|
41699
|
-
async function runStudioAdd(options, io) {
|
|
41700
|
-
const schema = await resolveSchema(options, io);
|
|
41701
|
-
const { files } = scaffold(schema, options);
|
|
41702
|
-
return {
|
|
41703
|
-
schema,
|
|
41704
|
-
written: await writeAll(files, io),
|
|
41705
|
-
verify: await verifyScaffold(files)
|
|
41706
|
-
};
|
|
41707
|
-
}
|
|
41708
|
-
/**
|
|
41709
|
-
* Scaffold a whole multi-entity app - every entity's screen plus a nav layout
|
|
41710
|
-
* and home page - and merge-write it. The `--all` path behind `svgrid-studio`.
|
|
41711
|
-
*/
|
|
41712
|
-
async function runStudioAddApp(schemas, options, io) {
|
|
41713
|
-
const { files } = scaffoldApp(schemas, options);
|
|
41714
|
-
return {
|
|
41715
|
-
written: await writeAll(files, io),
|
|
41716
|
-
verify: await verifyScaffold(files)
|
|
41717
|
-
};
|
|
41718
|
-
}
|
|
41719
|
-
//#endregion
|
|
41720
41824
|
//#region src/revoked.ts
|
|
41721
41825
|
var REVOKED_KEYS = /* @__PURE__ */ new Set([]);
|
|
41722
41826
|
/**
|
package/dist/server/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export { adaptCallbackDatasource, toCallbackDatasource, fromCallbackRequest, toC
|
|
|
16
16
|
export { createServerGroupModel, serverGroupRows, serverGroupNav, type ServerGroupController, type ServerGroupControllerOptions, type ServerGroupState, type ServerGroupGridRow, } from './server-group-model.js';
|
|
17
17
|
export { createServerSelectionModel, type ServerSelectionModel, type ServerSelectionModelOptions, type ServerSelectionState, type ServerGroupSelectionNode, type ServerSelectionGroupMode, } from './server-selection.js';
|
|
18
18
|
export { buildPivotResultColumns, type PivotResultColumnOptions, } from './server-pivot.js';
|
|
19
|
+
export { createWorkerDataSource, type WorkerDataSource, type WorkerDataSourceOptions, type WorkerLike, } from './worker-data-source.js';
|
|
19
20
|
export { enableServerRowModel } from './enable.js';
|
|
20
21
|
export { defaultServerGroupMessages, resolveServerGroupMessages, type ServerGroupMessages, } from './messages.js';
|
|
21
22
|
export { default as SvGroupCell } from './SvGroupCell.svelte';
|
package/dist/server/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export { adaptCallbackDatasource, toCallbackDatasource, fromCallbackRequest, toC
|
|
|
18
18
|
export { createServerGroupModel, serverGroupRows, serverGroupNav, } from './server-group-model.js';
|
|
19
19
|
export { createServerSelectionModel, } from './server-selection.js';
|
|
20
20
|
export { buildPivotResultColumns, } from './server-pivot.js';
|
|
21
|
+
export { createWorkerDataSource, } from './worker-data-source.js';
|
|
21
22
|
export { enableServerRowModel } from './enable.js';
|
|
22
23
|
export { defaultServerGroupMessages, resolveServerGroupMessages, } from './messages.js';
|
|
23
24
|
export { default as SvGroupCell } from './SvGroupCell.svelte';
|
|
@@ -169,6 +169,15 @@ export type ServerRowModelOptions<TData> = {
|
|
|
169
169
|
* grid's pinned rows so it stays in view.
|
|
170
170
|
*/
|
|
171
171
|
grandTotalRow?: 'top' | 'bottom' | 'pinnedTop' | 'pinnedBottom';
|
|
172
|
+
/**
|
|
173
|
+
* Milliseconds to keep the rows on screen after a sort, a filter or a
|
|
174
|
+
* regroup while the new answer loads. If its first block lands in that
|
|
175
|
+
* time, the grid goes straight from the old rows to the new ones: no
|
|
176
|
+
* frame of skeletons between them, and one update instead of two. A fast
|
|
177
|
+
* source (a worker, a local database) never shows a skeleton; a slow one
|
|
178
|
+
* shows them once the time is up. Default 0: skeletons at once.
|
|
179
|
+
*/
|
|
180
|
+
keepRowsWhileLoading?: number;
|
|
172
181
|
/** Drop a group's cached children when it is collapsed. Default false. */
|
|
173
182
|
purgeClosedGroups?: boolean;
|
|
174
183
|
/**
|