@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.
Files changed (78) hide show
  1. package/dist/SvSheet.svelte +93 -8
  2. package/dist/cdn/svgrid-enterprise.svelte-external.js +9339 -9077
  3. package/dist/designer/assets/{GridMenus-8gTNssgV.js → GridMenus-DGZJKFvX.js} +1 -1
  4. package/dist/designer/assets/SvGrid.helpers-DE5mmORW.js +1 -0
  5. package/dist/designer/assets/{SvGridCellEditor-DMoWJjEI.js → SvGridCellEditor-C7NWcnNC.js} +1 -1
  6. package/dist/designer/assets/{SvGridChart-Cy1ATrWh.js → SvGridChart-Fg43Yi0M.js} +2 -2
  7. package/dist/designer/assets/{SvGridChartBuilder-eQ3mn0L9.js → SvGridChartBuilder-BqGuA3yU.js} +1 -1
  8. package/dist/designer/assets/{SvGridChartPanel-CATdVc9X.js → SvGridChartPanel-BsiXRAnx.js} +3 -3
  9. package/dist/designer/assets/{SvGridChartView-CmsZtCED.js → SvGridChartView-BgttK0tR.js} +1 -1
  10. package/dist/designer/assets/{SvListBox-BDS66lmZ.js → SvListBox-v2jm0-vf.js} +1 -1
  11. package/dist/designer/assets/cell-formatting-BWrW_O0n.js +1 -0
  12. package/dist/designer/assets/{chart-Cn7MnNqA.js → chart-BIl-dIRv.js} +1 -1
  13. package/dist/designer/assets/{chart-panel-messages-Dz7PTIqT.js → chart-panel-messages-4cF2vw7L.js} +1 -1
  14. package/dist/designer/assets/{chart-summary-BD1qYB2R.js → chart-summary-B7bTtSCC.js} +1 -1
  15. package/dist/designer/assets/{chart-trend-B8xr5LuD.js → chart-trend-CLsn5Hgd.js} +1 -1
  16. package/dist/designer/assets/export-format-DGscOjS9.js +6 -0
  17. package/dist/designer/assets/{index-BSjv6Qec.js → index-V_cMXdxt.js} +314 -314
  18. package/dist/designer/assets/index-l21iGkpF.css +1 -0
  19. package/dist/designer/assets/src-Mu7AGqT8.js +58 -0
  20. package/dist/designer/assets/validate-YxSrBF1H.js +1 -0
  21. package/dist/designer/index.html +12 -12
  22. package/dist/export-ooxml.d.ts +10 -0
  23. package/dist/export-ooxml.js +22 -5
  24. package/dist/import.js +11 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +1 -1
  27. package/dist/node/studio.js +236 -132
  28. package/dist/server/index.d.ts +1 -0
  29. package/dist/server/index.js +1 -0
  30. package/dist/server/server-row-model.d.ts +9 -0
  31. package/dist/server/server-row-model.js +120 -3
  32. package/dist/server/worker-data-source.d.ts +62 -0
  33. package/dist/server/worker-data-source.js +133 -0
  34. package/dist/sheet/objects.d.ts +47 -0
  35. package/dist/sheet/objects.js +33 -0
  36. package/dist/studio/cli.js +55 -1
  37. package/dist/studio/emit-project.js +8 -3
  38. package/dist/studio/init-flow.d.ts +6 -1
  39. package/dist/studio/init-flow.js +43 -2
  40. package/dist/studio/scaffold-app.d.ts +6 -0
  41. package/dist/studio/scaffold-app.js +5 -3
  42. package/dist/studio/scaffold.js +29 -12
  43. package/dist/sveltekit/in-memory.d.ts +21 -1
  44. package/dist/sveltekit/in-memory.js +153 -105
  45. package/dist/sveltekit/index.d.ts +1 -1
  46. package/dist/version.d.ts +14 -1
  47. package/dist/version.js +14 -1
  48. package/dist/wc/{GridMenus-By9vPuq0.js → GridMenus-6_fF5jIm.js} +2 -2
  49. package/dist/wc/{SvGrid.helpers-BdVDtjUm.js → SvGrid.helpers-zBkDTryR.js} +36 -37
  50. package/dist/wc/{SvGridCellEditor-Bnf1Kr5C.js → SvGridCellEditor-CzUKEbkI.js} +1 -1
  51. package/dist/wc/{SvGridChart-BqUJh2pG.js → SvGridChart-C6UvNIbN.js} +3 -3
  52. package/dist/wc/{SvGridChartBuilder-CGycC5cn.js → SvGridChartBuilder-ikf_5LyW.js} +3 -3
  53. package/dist/wc/{SvGridChartPanel-CRnizsHD.js → SvGridChartPanel-Dc_jj7B-.js} +4 -4
  54. package/dist/wc/{SvGridChartView-D1pa_xMy.js → SvGridChartView-thkznAOb.js} +2 -2
  55. package/dist/wc/{cell-formatting-CBfgahv1.js → cell-formatting-hb3vzxhJ.js} +27 -5
  56. package/dist/wc/{chart-6OXlybVT.js → chart-CV5D7_k8.js} +1 -1
  57. package/dist/wc/{chart-summary-2m3ia1-n.js → chart-summary-DGx0QhJa.js} +1 -1
  58. package/dist/wc/{chart-trend-CQOQtTMl.js → chart-trend-BIRH_xH8.js} +1 -1
  59. package/dist/wc/sv-sheet-element.js +7454 -7352
  60. package/dist/wc/{svelte-virtualizer.svelte-CXtQy2TQ.js → svelte-virtualizer.svelte-Du4tjrIP.js} +7 -3
  61. package/dist/wc/{validate-BqDthkNr.js → validate-JAuMsk6Z.js} +36 -36
  62. package/dist/worker/columnar.d.ts +42 -0
  63. package/dist/worker/columnar.js +410 -0
  64. package/dist/worker/index.d.ts +12 -0
  65. package/dist/worker/index.js +12 -0
  66. package/dist/worker/infer-schema.d.ts +21 -0
  67. package/dist/worker/infer-schema.js +34 -0
  68. package/dist/worker/protocol.d.ts +73 -0
  69. package/dist/worker/protocol.js +14 -0
  70. package/dist/worker/serve.d.ts +44 -0
  71. package/dist/worker/serve.js +100 -0
  72. package/package.json +7 -3
  73. package/dist/designer/assets/SvGrid.helpers-BgT-gnCo.js +0 -1
  74. package/dist/designer/assets/cell-formatting-BbubJTVz.js +0 -1
  75. package/dist/designer/assets/export-format-DGckmgKr.js +0 -6
  76. package/dist/designer/assets/index-_JVFxt02.css +0 -1
  77. package/dist/designer/assets/src-DUQ4xE3x.js +0 -58
  78. package/dist/designer/assets/validate-C3B8QKsM.js +0 -1
@@ -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
- let state = $state<ServerState<${rowType}>>({
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) => (state = 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(state.pageCount - 1) // jump to the new row (appended at the end)
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 state.error}<p class="page__error" role="alert">{state.error}</p>{/if}
1790
+ {#if view.error}<p class="page__error" role="alert">{view.error}</p>{/if}
1791
1791
 
1792
1792
  <SvGrid
1793
- data={state.rows}
1793
+ data={view.rows}
1794
1794
  {columns}
1795
- loading={state.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={state.total}
1819
- pageIndex={state.pageIndex}
1820
- pageSize={state.pageSize}
1818
+ rowCount={view.total}
1819
+ pageIndex={view.pageIndex}
1820
+ pageSize={view.pageSize}
1821
1821
  onPaginationChange={({ pageIndex, pageSize }) => {
1822
- if (pageSize !== state.pageSize) controller.setPageSize(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
- files.push(layoutFile$1(nav, appTitle, {
2022
+ const theme = {
2007
2023
  preset: options.theme,
2008
2024
  mode: options.dark ? "dark" : "light"
2009
- }), homeFile$1(nav, appTitle));
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.0";
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": svgrid,
20891
- "@svgrid/enterprise": svgrid
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$1(files, outDir, io) {
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$1(emitStudioAppBundle(project), outDir, io);
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
  /**
@@ -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';
@@ -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
  /**