@svgrid/enterprise 3.1.4 → 3.1.5
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/cdn/svgrid-enterprise.svelte-external.js +8956 -8606
- package/dist/designer/assets/GridMenus-CvcASlKv.js +8 -0
- package/dist/designer/assets/SvListBox-BlmmzYcU.js +110 -0
- package/dist/designer/assets/{index-BgVTSVoK.js → index-B3WBezq_.js} +432 -370
- package/dist/designer/assets/index-KmSEY7Lb.css +1 -0
- package/dist/designer/assets/src-D4PRZJYA.js +58 -0
- package/dist/designer/assets/{validate-YxSrBF1H.js → validate-B71dE1Cs.js} +1 -1
- package/dist/designer/index.html +4 -4
- package/dist/node/studio.js +402 -81
- package/dist/server/server-row-model.js +199 -26
- package/dist/studio/emit-project.js +86 -56
- package/dist/studio/emit-schema.d.ts +3 -0
- package/dist/studio/emit-schema.js +128 -29
- package/dist/studio/index.d.ts +2 -1
- package/dist/studio/index.js +2 -1
- package/dist/studio/init-flow.d.ts +3 -0
- package/dist/studio/init-flow.js +5 -2
- package/dist/studio/project.d.ts +10 -1
- package/dist/studio/project.js +28 -15
- package/dist/studio/samples/restaurant.js +1 -1
- package/dist/studio/sveltekit3.d.ts +38 -0
- package/dist/studio/sveltekit3.js +194 -0
- package/dist/studio/ui-components.generated.js +15 -4
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/dist/watermark.js +31 -1
- package/dist/wc/{GridMenus-6_fF5jIm.js → GridMenus-fWbBUiRH.js} +121 -121
- package/dist/wc/sv-sheet-element.js +10979 -10522
- package/dist/wc/{svelte-virtualizer.svelte-Du4tjrIP.js → svelte-virtualizer.svelte-Dntdh8Ms.js} +103 -56
- package/dist/wc/{validate-JAuMsk6Z.js → validate-43C03DLP.js} +1 -1
- package/package.json +3 -3
- package/dist/designer/assets/GridMenus-DGZJKFvX.js +0 -8
- package/dist/designer/assets/SvListBox-v2jm0-vf.js +0 -110
- package/dist/designer/assets/index-l21iGkpF.css +0 -1
- package/dist/designer/assets/src-BqEXoOog.js +0 -58
package/dist/node/studio.js
CHANGED
|
@@ -2027,6 +2027,181 @@ function scaffoldApp(schemas, options = {}) {
|
|
|
2027
2027
|
return { files };
|
|
2028
2028
|
}
|
|
2029
2029
|
//#endregion
|
|
2030
|
+
//#region src/studio/sveltekit3.ts
|
|
2031
|
+
/** Toolchain SvelteKit 3 requires. Minimums are from the v3 migration guide. */
|
|
2032
|
+
var SVELTEKIT_3_DEV_DEPENDENCIES = {
|
|
2033
|
+
"@sveltejs/kit": "^3.0.0",
|
|
2034
|
+
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
2035
|
+
svelte: "^5.57.1",
|
|
2036
|
+
"svelte-check": "^4.7.5",
|
|
2037
|
+
typescript: "^6.0.0",
|
|
2038
|
+
vite: "^8.0.12"
|
|
2039
|
+
};
|
|
2040
|
+
/**
|
|
2041
|
+
* The first major of each adapter whose peerDependency is `@sveltejs/kit ^3`,
|
|
2042
|
+
* read from npm on 2026-10-08. None of the v3 adapter behaviour changes (node's
|
|
2043
|
+
* ORIGIN, vercel's edge runtime, cloudflare's platform APIs) reach Studio
|
|
2044
|
+
* output, so this is a version bump only.
|
|
2045
|
+
*/
|
|
2046
|
+
var SVELTEKIT_3_ADAPTERS = {
|
|
2047
|
+
"@sveltejs/adapter-auto": "^8.0.0",
|
|
2048
|
+
"@sveltejs/adapter-node": "^6.0.0",
|
|
2049
|
+
"@sveltejs/adapter-vercel": "^7.0.0",
|
|
2050
|
+
"@sveltejs/adapter-netlify": "^7.0.1",
|
|
2051
|
+
"@sveltejs/adapter-cloudflare": "^8.0.0"
|
|
2052
|
+
};
|
|
2053
|
+
var SOURCE = /\.(ts|js|svelte)$/;
|
|
2054
|
+
function toSvelteKit3(files) {
|
|
2055
|
+
const paths = new Set(files.map((f) => f.path));
|
|
2056
|
+
const adapter = adapterOf(files);
|
|
2057
|
+
const envNames = /* @__PURE__ */ new Set();
|
|
2058
|
+
const out = [];
|
|
2059
|
+
for (const file of files) {
|
|
2060
|
+
if (file.path === "svelte.config.js") continue;
|
|
2061
|
+
if (file.path === "package.json") {
|
|
2062
|
+
out.push({
|
|
2063
|
+
...file,
|
|
2064
|
+
contents: packageJson3(file.contents)
|
|
2065
|
+
});
|
|
2066
|
+
continue;
|
|
2067
|
+
}
|
|
2068
|
+
if (file.path === "vite.config.ts") {
|
|
2069
|
+
out.push({
|
|
2070
|
+
...file,
|
|
2071
|
+
description: `Vite config, including SvelteKit (${adapter}).`,
|
|
2072
|
+
contents: viteConfig3(file.contents, adapter)
|
|
2073
|
+
});
|
|
2074
|
+
continue;
|
|
2075
|
+
}
|
|
2076
|
+
if (file.path === "tsconfig.json") {
|
|
2077
|
+
out.push({
|
|
2078
|
+
...file,
|
|
2079
|
+
contents: TSCONFIG_3
|
|
2080
|
+
});
|
|
2081
|
+
continue;
|
|
2082
|
+
}
|
|
2083
|
+
if (SOURCE.test(file.path) && file.path !== "drizzle.config.ts") {
|
|
2084
|
+
const contents = rewriteSource(file.contents, paths, envNames);
|
|
2085
|
+
out.push(contents === file.contents ? file : {
|
|
2086
|
+
...file,
|
|
2087
|
+
contents
|
|
2088
|
+
});
|
|
2089
|
+
continue;
|
|
2090
|
+
}
|
|
2091
|
+
out.push(file);
|
|
2092
|
+
}
|
|
2093
|
+
if (envNames.size) out.push(envModule([...envNames].sort()));
|
|
2094
|
+
return out;
|
|
2095
|
+
}
|
|
2096
|
+
function adapterOf(files) {
|
|
2097
|
+
const pkg = files.find((f) => f.path === "package.json");
|
|
2098
|
+
if (!pkg) return "@sveltejs/adapter-auto";
|
|
2099
|
+
const dev = JSON.parse(pkg.contents).devDependencies ?? {};
|
|
2100
|
+
return Object.keys(dev).find((k) => k.startsWith("@sveltejs/adapter-")) ?? "@sveltejs/adapter-auto";
|
|
2101
|
+
}
|
|
2102
|
+
function packageJson3(contents) {
|
|
2103
|
+
const pkg = JSON.parse(contents);
|
|
2104
|
+
const dev = { ...pkg.devDependencies ?? {} };
|
|
2105
|
+
for (const [name, range] of Object.entries(SVELTEKIT_3_DEV_DEPENDENCIES)) dev[name] = range;
|
|
2106
|
+
for (const name of Object.keys(dev)) if (SVELTEKIT_3_ADAPTERS[name]) dev[name] = SVELTEKIT_3_ADAPTERS[name];
|
|
2107
|
+
pkg.devDependencies = dev;
|
|
2108
|
+
pkg.imports = {
|
|
2109
|
+
"#lib": "./src/lib/index.js",
|
|
2110
|
+
"#lib/*": "./src/lib/*"
|
|
2111
|
+
};
|
|
2112
|
+
return JSON.stringify(pkg, null, 2) + "\n";
|
|
2113
|
+
}
|
|
2114
|
+
/**
|
|
2115
|
+
* The v2 vite.config.ts with SvelteKit's config folded into the plugin call.
|
|
2116
|
+
* Everything else in the file (the dev-server watch exclusions) is kept.
|
|
2117
|
+
*/
|
|
2118
|
+
function viteConfig3(v2, adapter) {
|
|
2119
|
+
const imports = `import adapter from '${adapter}'\nimport { vitePreprocess } from '@sveltejs/vite-plugin-svelte'\n`;
|
|
2120
|
+
const withPlugin = v2.replace("sveltekit()", "sveltekit({ preprocess: vitePreprocess(), adapter: adapter() })");
|
|
2121
|
+
if (withPlugin === v2) throw new Error("toSvelteKit3: vite.config.ts has no sveltekit() call to configure");
|
|
2122
|
+
return imports + withPlugin;
|
|
2123
|
+
}
|
|
2124
|
+
var TSCONFIG_3 = `{
|
|
2125
|
+
"extends": "$app/tsconfig",
|
|
2126
|
+
"compilerOptions": {
|
|
2127
|
+
"sourceMap": true,
|
|
2128
|
+
"strict": true
|
|
2129
|
+
},
|
|
2130
|
+
"include": ["src", "vite.config.ts"]
|
|
2131
|
+
}
|
|
2132
|
+
`;
|
|
2133
|
+
function rewriteSource(src, paths, envNames) {
|
|
2134
|
+
let s = src;
|
|
2135
|
+
s = rewriteLibImports(s, paths);
|
|
2136
|
+
s = rewriteEnv(s, envNames);
|
|
2137
|
+
s = rewriteKitImports(s);
|
|
2138
|
+
s = s.replace(/\{\s*keepFocus:\s*true,\s*noScroll:\s*true\s*\}/g, "{ reset: false }");
|
|
2139
|
+
s = s.replace(/new URLSearchParams\(page\.url\.searchParams\)/g, "new URLSearchParams(page.url.search)");
|
|
2140
|
+
return s;
|
|
2141
|
+
}
|
|
2142
|
+
/**
|
|
2143
|
+
* `$lib/x` -> `#lib/x.js`. Subpath imports need a real extension, and the right
|
|
2144
|
+
* one depends on what the file actually is, so each specifier is resolved
|
|
2145
|
+
* against the generated paths rather than guessed: `x.ts` and `x.svelte.ts`
|
|
2146
|
+
* are imported as `.js` (TypeScript maps it back), anything already carrying
|
|
2147
|
+
* its extension (`.svelte`, `.js`) is left as it is, and a directory resolves
|
|
2148
|
+
* to its index.
|
|
2149
|
+
*/
|
|
2150
|
+
function rewriteLibImports(src, paths) {
|
|
2151
|
+
return src.replace(/(['"`])\$lib\/([^'"`]+)\1/g, (whole, q, spec) => {
|
|
2152
|
+
const base = `src/lib/${spec}`;
|
|
2153
|
+
if (paths.has(base)) return `${q}#lib/${spec}${q}`;
|
|
2154
|
+
if (paths.has(`${base}.ts`) || paths.has(`${base}.js`)) return `${q}#lib/${spec}.js${q}`;
|
|
2155
|
+
if (paths.has(`${base}/index.ts`) || paths.has(`${base}/index.js`)) return `${q}#lib/${spec}/index.js${q}`;
|
|
2156
|
+
return `${q}#lib/${spec}.js${q}`;
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
/**
|
|
2160
|
+
* `import { env } from '$env/dynamic/private'` + `env.X`
|
|
2161
|
+
* -> `import { X } from '$app/env/private'` + `X`
|
|
2162
|
+
* Every name read is collected so src/env.ts can declare it, which v3 requires.
|
|
2163
|
+
*/
|
|
2164
|
+
function rewriteEnv(src, envNames) {
|
|
2165
|
+
if (!/from\s+['"]\$env\/dynamic\/private['"]/.test(src)) return src;
|
|
2166
|
+
const used = /* @__PURE__ */ new Set();
|
|
2167
|
+
const body = src.replace(/(?<![.\w])env\.([A-Z_][A-Z0-9_]*)/g, (_m, name) => {
|
|
2168
|
+
used.add(name);
|
|
2169
|
+
return name;
|
|
2170
|
+
});
|
|
2171
|
+
for (const n of used) envNames.add(n);
|
|
2172
|
+
const names = [...used].sort();
|
|
2173
|
+
return body.replace(/import\s*\{\s*env\s*\}\s*from\s*(['"])\$env\/dynamic\/private\1/, names.length ? `import { ${names.join(", ")} } from '$app/env/private'` : "");
|
|
2174
|
+
}
|
|
2175
|
+
/**
|
|
2176
|
+
* Types that moved out of `@sveltejs/kit`, and the deprecated `json` helper.
|
|
2177
|
+
* `fail`, `redirect` and `error` stay where they are.
|
|
2178
|
+
*/
|
|
2179
|
+
function rewriteKitImports(src) {
|
|
2180
|
+
let s = src;
|
|
2181
|
+
s = s.replace(/import\s+type\s*\{\s*Handle\s*\}\s*from\s*(['"])@sveltejs\/kit\1/g, `import type { Handle } from '@sveltejs/kit/hooks'`);
|
|
2182
|
+
s = s.replace(/import\s+type\s*\{\s*SubmitFunction\s*\}\s*from\s*(['"])@sveltejs\/kit\1/g, `import type { SubmitFunction } from '$app/forms'`);
|
|
2183
|
+
const kitImport = /import\s*\{([^}]*)\}\s*from\s*(['"])@sveltejs\/kit\2/;
|
|
2184
|
+
const names = (s.match(kitImport)?.[1] ?? "").split(",").map((x) => x.trim());
|
|
2185
|
+
if (names.includes("json")) {
|
|
2186
|
+
s = s.replace(/(?<![.\w])json\(/g, "Response.json(");
|
|
2187
|
+
const rest = names.filter((x) => x && x !== "json");
|
|
2188
|
+
s = s.replace(kitImport, rest.length ? `import { ${rest.join(", ")} } from '@sveltejs/kit'` : "");
|
|
2189
|
+
}
|
|
2190
|
+
return s;
|
|
2191
|
+
}
|
|
2192
|
+
function envModule(names) {
|
|
2193
|
+
return {
|
|
2194
|
+
path: "src/env.ts",
|
|
2195
|
+
description: "Environment variables the app reads (SvelteKit 3 requires declaring them).",
|
|
2196
|
+
contents: `import { defineEnvVars } from '@sveltejs/kit/env'
|
|
2197
|
+
|
|
2198
|
+
// SvelteKit 3 reads only the variables declared here. A missing one comes
|
|
2199
|
+
// through as '' - every read in this app already falls back on that
|
|
2200
|
+
// (a dev default, '?? ""', or an if), so that is the intended behaviour.
|
|
2201
|
+
export const variables = defineEnvVars({\n${names.map((n) => ` ${n}: { schema: (input) => input ?? '' },`).join("\n")}\n})\n`
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
//#endregion
|
|
2030
2205
|
//#region src/expressions/parse.ts
|
|
2031
2206
|
/** Every column id referenced anywhere in a predicate (deduped, in order). */
|
|
2032
2207
|
function collectColumnRefs(expr) {
|
|
@@ -8888,14 +9063,14 @@ var GENERATED_UI_SURFACE = {
|
|
|
8888
9063
|
"key": "columnVirtualization",
|
|
8889
9064
|
"label": "Column Virtualization",
|
|
8890
9065
|
"type": "boolean",
|
|
8891
|
-
"description": "Render only the columns in view, the horizontal counterpart of `virtualization`. **On by default**, which is what makes a 100-column grid scroll smoothly.
|
|
9066
|
+
"description": "Render only the columns in view, the horizontal counterpart of `virtualization`. **On by default**, which is what makes a 100-column grid scroll smoothly. Pinned columns stay rendered while it is on: the pinned-left and pinned-right columns are drawn as their own runs either side of the virtual window, so a wide grid with pinned edges still renders only the columns in view plus the pinned ones.",
|
|
8892
9067
|
"group": "common"
|
|
8893
9068
|
},
|
|
8894
9069
|
{
|
|
8895
9070
|
"key": "columnOverscan",
|
|
8896
9071
|
"label": "Column Overscan",
|
|
8897
9072
|
"type": "number",
|
|
8898
|
-
"description": "
|
|
9073
|
+
"description": "Columns kept rendered beyond the viewport when `columnVirtualization` is on: this many ahead of a horizontal scroll at all times, one behind it. The rendered columns move three at a time, so right after a move up to `columnOverscan + 2` are rendered ahead. Defaults to 3.",
|
|
8899
9074
|
"group": "common"
|
|
8900
9075
|
},
|
|
8901
9076
|
{
|
|
@@ -8909,7 +9084,7 @@ var GENERATED_UI_SURFACE = {
|
|
|
8909
9084
|
"key": "initialColumnPinning",
|
|
8910
9085
|
"label": "Initial Column Pinning",
|
|
8911
9086
|
"type": "json",
|
|
8912
|
-
"description": "Columns pinned to the left/right edge on mount. Each entry is a column id (matches `ColumnDef.field` when no explicit id is set). The internal pinning state is seeded once at mount; user-driven pinning via the column menu still works and overrides this default.
|
|
9087
|
+
"description": "Columns pinned to the left/right edge on mount. Each entry is a column id (matches `ColumnDef.field` when no explicit id is set). The internal pinning state is seeded once at mount; user-driven pinning via the column menu still works and overrides this default. Works with column virtualization on (the default).",
|
|
8913
9088
|
"group": "common"
|
|
8914
9089
|
},
|
|
8915
9090
|
{
|
|
@@ -9187,7 +9362,7 @@ var GENERATED_UI_SURFACE = {
|
|
|
9187
9362
|
"key": "conditionalFormats",
|
|
9188
9363
|
"label": "Conditional Formats",
|
|
9189
9364
|
"type": "json",
|
|
9190
|
-
"description": "Excel-style conditional formatting. A list of value-driven rules that color cells: `colorScale` (gradient across the column range), `dataBar` (in-cell proportional bar), `iconSet` (arrows / traffic / triangles by threshold), and `rule` (apply a style when a predicate matches). Scope a format to specific columns with `columns: [...]`, or omit it to apply to every column. Later entries win on conflict.",
|
|
9365
|
+
"description": "Excel-style conditional formatting. A list of value-driven rules that color cells: `colorScale` (gradient across the column range), `dataBar` (in-cell proportional bar), `iconSet` (arrows / traffic / triangles by threshold), and `rule` (apply a style when a predicate matches). Scope a format to specific columns with `columns: [...]`, or omit it to apply to every column. Later entries win on conflict. `colorScale` and `dataBar` apply to data rows only: group, footer and grand-total rows carry subtotals on a different scale, so they keep the `rule` and `iconSet` formats and drop the scaled ones. On a server row model, give scaled formats `minValue` / `maxValue`; the grid does not scan rows it has not loaded for a range.",
|
|
9191
9366
|
"group": "common"
|
|
9192
9367
|
},
|
|
9193
9368
|
{
|
|
@@ -9311,6 +9486,14 @@ var GENERATED_UI_SURFACE = {
|
|
|
9311
9486
|
"description": "Merged cells: rectangles drawn as one cell, a spreadsheet's Merge & Center. Each entry is the top-left cell (display indices) and how many rows and columns it covers. The origin's td takes the span and shows the origin's value; the covered cells are not drawn, a selection grows to whole merges, the active cell inside a merge is its origin, and the arrow keys step over a merge as one cell. A merge that crosses the frozen boundary or the rendered window is drawn in parts, one per band. The grid does not write into covered cells on its own; a consumer that merges cells keeps them empty or marks them read-only through the column's `editable`.",
|
|
9312
9487
|
"group": "common"
|
|
9313
9488
|
},
|
|
9489
|
+
{
|
|
9490
|
+
"key": "rowLayout",
|
|
9491
|
+
"label": "Row Layout",
|
|
9492
|
+
"type": "select",
|
|
9493
|
+
"options": ["flex", "table"],
|
|
9494
|
+
"description": "How rows are laid out. `'flex'` (the default) draws each row as a line of fixed-width cells, so adding or removing cells in one row - what a horizontal scroll does every frame - lays out that row only. `'table'` uses the HTML table algorithm, which re-lays every row on such a change but can draw a merged cell spanning rows; the grid switches to it on its own when `mergedCells` has one.",
|
|
9495
|
+
"group": "common"
|
|
9496
|
+
},
|
|
9314
9497
|
{
|
|
9315
9498
|
"key": "pinnedBottomRows",
|
|
9316
9499
|
"label": "Pinned Bottom Rows",
|
|
@@ -12115,13 +12298,14 @@ function ssrScreenShape(project, screen) {
|
|
|
12115
12298
|
function ssrEligible(project, screen) {
|
|
12116
12299
|
return ssrScreenShape(project, screen) !== null;
|
|
12117
12300
|
}
|
|
12118
|
-
/** Set a screen's render mode
|
|
12301
|
+
/** Set a screen's render mode. Both modes are stored: a screen switched to
|
|
12302
|
+
* 'spa' stays client-rendered even where `withSsrDefaults` would turn it on. */
|
|
12119
12303
|
function setScreenRenderMode(project, screenId, mode) {
|
|
12120
12304
|
return {
|
|
12121
12305
|
...project,
|
|
12122
12306
|
screens: project.screens.map((s) => s.id === screenId ? {
|
|
12123
12307
|
...s,
|
|
12124
|
-
renderMode: mode
|
|
12308
|
+
renderMode: mode
|
|
12125
12309
|
} : s)
|
|
12126
12310
|
};
|
|
12127
12311
|
}
|
|
@@ -12130,19 +12314,23 @@ function isSsrScreen(project, screen) {
|
|
|
12130
12314
|
return screenRenderMode(project, screen) === "ssr" && ssrEligible(project, screen);
|
|
12131
12315
|
}
|
|
12132
12316
|
/**
|
|
12133
|
-
* Turn server rendering on for every screen that can support it
|
|
12134
|
-
*
|
|
12135
|
-
*
|
|
12136
|
-
*
|
|
12137
|
-
* overrides a choice somebody made.
|
|
12317
|
+
* Turn server rendering on for every screen that can support it. Applied when a
|
|
12318
|
+
* project is first generated, so a new app reads as idiomatic SvelteKit (a
|
|
12319
|
+
* `load` and form `actions`) rather than a client SPA. Screens that already
|
|
12320
|
+
* state a mode are left alone, so this never overrides a choice somebody made.
|
|
12138
12321
|
*
|
|
12139
|
-
*
|
|
12140
|
-
*
|
|
12141
|
-
*
|
|
12142
|
-
*
|
|
12143
|
-
*
|
|
12322
|
+
* Limited to sources that hold one copy of the data. SQL and REST always did:
|
|
12323
|
+
* every path goes to the same database or the same remote API. In-memory rows
|
|
12324
|
+
* are held once on the server ($lib/server/store) and every screen reaches them
|
|
12325
|
+
* through the entity's /api route, so an SSR screen and an SPA screen see the
|
|
12326
|
+
* same rows. PGlite lives in the browser's IndexedDB, which the server cannot
|
|
12327
|
+
* read, so it stays client-rendered.
|
|
12144
12328
|
*/
|
|
12145
|
-
var SSR_DEFAULT_KINDS = /* @__PURE__ */ new Set([
|
|
12329
|
+
var SSR_DEFAULT_KINDS = /* @__PURE__ */ new Set([
|
|
12330
|
+
"sql",
|
|
12331
|
+
"rest",
|
|
12332
|
+
"memory"
|
|
12333
|
+
]);
|
|
12146
12334
|
function withSsrDefaults(project) {
|
|
12147
12335
|
return {
|
|
12148
12336
|
...project,
|
|
@@ -14170,6 +14358,18 @@ function setDataSource(project, dataSource) {
|
|
|
14170
14358
|
};
|
|
14171
14359
|
}
|
|
14172
14360
|
/** Set the deploy target (SvelteKit adapter + provider config the bundle emits). */
|
|
14361
|
+
/** Target a SvelteKit major. 2 is the default and is stored as the field's
|
|
14362
|
+
* absence, the same way `deploy: 'auto'` is. */
|
|
14363
|
+
function setKitVersion(project, kit) {
|
|
14364
|
+
if (kit === 2) {
|
|
14365
|
+
const { kit: _drop, ...rest } = project;
|
|
14366
|
+
return rest;
|
|
14367
|
+
}
|
|
14368
|
+
return {
|
|
14369
|
+
...project,
|
|
14370
|
+
kit: 3
|
|
14371
|
+
};
|
|
14372
|
+
}
|
|
14173
14373
|
function setDeployTarget(project, deploy) {
|
|
14174
14374
|
if (deploy === "auto") {
|
|
14175
14375
|
const { deploy: _drop, ...rest } = project;
|
|
@@ -14458,6 +14658,7 @@ function parseProject(json) {
|
|
|
14458
14658
|
...typeof p.audit === "boolean" ? { audit: p.audit } : {},
|
|
14459
14659
|
...p.i18n && typeof p.i18n === "object" ? { i18n: p.i18n } : {},
|
|
14460
14660
|
...typeof p.deploy === "string" && p.deploy !== "auto" ? { deploy: p.deploy } : {},
|
|
14661
|
+
...p.kit === 3 ? { kit: 3 } : {},
|
|
14461
14662
|
...p.triggers && typeof p.triggers === "object" ? { triggers: p.triggers } : {}
|
|
14462
14663
|
});
|
|
14463
14664
|
}
|
|
@@ -15287,35 +15488,18 @@ var SQL_DRIVERS = {
|
|
|
15287
15488
|
exec: `const rs = await client.execute({ sql: text, args: params })\n return rs.rows as unknown as Record<string, unknown>[]`
|
|
15288
15489
|
}
|
|
15289
15490
|
};
|
|
15290
|
-
/** A fully-connected SvelteKit API route for a SQL-bound entity. When RBAC is on,
|
|
15291
|
-
* the route imports the shared access policy and rejects unauthorized writes -
|
|
15292
|
-
* server-enforced, so a tampered client can't bypass it. When audit is on, every
|
|
15293
|
-
* successful write is recorded. */
|
|
15294
15491
|
function sqlRouteFile(schema, table, dialect, feat = {}) {
|
|
15295
15492
|
const n = namesFor(schema);
|
|
15296
15493
|
const driver = SQL_DRIVERS[dialect === "supabase" ? "postgres" : dialect ?? "postgres"];
|
|
15297
15494
|
const dialectLiteral = sqlDialectExpr(dialect);
|
|
15298
|
-
const
|
|
15299
|
-
const auditImport = feat.audit ? `\nimport { recordAudit } from '$lib/audit'` : "";
|
|
15300
|
-
const tenantImport = feat.tenantField ? `\nimport { requireTenant } from '$lib/server/tenant'` : "";
|
|
15301
|
-
const opts = [
|
|
15302
|
-
`schema: ${n.schemaVar}`,
|
|
15303
|
-
`source`,
|
|
15304
|
-
`validate: true`
|
|
15305
|
-
];
|
|
15306
|
-
if (feat.access) opts.push(`// Server-enforced RBAC: the caller's role comes from the session (event.locals). Reads\n // are allowed only if the role can open one of this entity's own screens.\n authorize: ({ action, event }) => authorizeAction(getServerRole(event), action, ${JSON.stringify(feat.screenIds ?? [])})`);
|
|
15307
|
-
if (feat.tenantField) opts.push(`// Multi-tenancy, enforced server-side: reads are filtered to the caller's tenant,\n // creates are stamped with it, and update/delete re-read the target under the\n // scope first - so a guessed id cannot reach another tenant's row.\n scope: ({ event }) => ({ field: ${JSON.stringify(feat.tenantField)}, value: requireTenant(event) })`);
|
|
15308
|
-
if (feat.audit) opts.push(`// Record every successful write to the audit trail.\n audit: (e) => recordAudit({ entity: ${JSON.stringify(schema.name)}, action: e.action, recordId: e.id, values: e.values as Record<string, unknown> | undefined, actor: String(e.event.locals?.role ?? e.event.locals?.user ?? 'system') })`);
|
|
15309
|
-
const hooks = triggerHooksOpt(feat.triggers);
|
|
15310
|
-
if (hooks) opts.push(`// Business rules, enforced server-side (a client that skips them still can't write bad data).\n ${hooks}`);
|
|
15311
|
-
const handlers = `export const { POST } = createKitHandlers({\n ${opts.join(",\n ")},\n})`;
|
|
15495
|
+
const { imports, handlers } = kitHandlersBlock(schema, feat);
|
|
15312
15496
|
return {
|
|
15313
15497
|
path: `src/routes/api/${n.route}/+server.ts`,
|
|
15314
15498
|
description: `Connected API route for ${n.label} (SQL via DATABASE_URL). Runs server-side.`,
|
|
15315
15499
|
contents: `${driver.imports}
|
|
15316
15500
|
import { env } from '$env/dynamic/private'
|
|
15317
15501
|
import { createKitHandlers, createSqlDataSource } from '@svgrid/enterprise'
|
|
15318
|
-
import { ${n.schemaVar}, type ${n.type} } from '$lib/schemas'${
|
|
15502
|
+
import { ${n.schemaVar}, type ${n.type} } from '$lib/schemas'${imports}
|
|
15319
15503
|
|
|
15320
15504
|
${driver.setup}
|
|
15321
15505
|
|
|
@@ -15331,6 +15515,99 @@ ${handlers}
|
|
|
15331
15515
|
`
|
|
15332
15516
|
};
|
|
15333
15517
|
}
|
|
15518
|
+
/**
|
|
15519
|
+
* The API route for an in-memory entity. Same handlers as a SQL route
|
|
15520
|
+
* (validation, RBAC, triggers, audit), over the one server-held copy of the
|
|
15521
|
+
* rows in `$lib/server/store`. Routing memory through the server like this is
|
|
15522
|
+
* what lets its screens render on the server: before, the rows were a module
|
|
15523
|
+
* singleton, so a server-rendered screen and a browser-rendered one each held
|
|
15524
|
+
* their own copy and never saw each other's writes.
|
|
15525
|
+
*/
|
|
15526
|
+
function memoryRouteFile(schema, feat = {}) {
|
|
15527
|
+
const n = namesFor(schema);
|
|
15528
|
+
const { imports, handlers } = kitHandlersBlock(schema, feat);
|
|
15529
|
+
return {
|
|
15530
|
+
path: `src/routes/api/${n.route}/+server.ts`,
|
|
15531
|
+
description: `API route for ${n.label} (in-memory rows held on the server). Runs server-side.`,
|
|
15532
|
+
contents: `import { createKitHandlers } from '@svgrid/enterprise'
|
|
15533
|
+
import { ${n.schemaVar} } from '$lib/schemas'
|
|
15534
|
+
import { ${storeVar(schema)} as source } from '$lib/server/store'${imports}
|
|
15535
|
+
|
|
15536
|
+
${handlers}
|
|
15537
|
+
`
|
|
15538
|
+
};
|
|
15539
|
+
}
|
|
15540
|
+
/**
|
|
15541
|
+
* `$lib/server/store.ts`: the seeded rows of every in-memory entity, held once
|
|
15542
|
+
* per server process. Only server code imports it (the API routes, scheduled
|
|
15543
|
+
* jobs), so the seed data stays out of the browser bundle.
|
|
15544
|
+
*
|
|
15545
|
+
* The server owns new ids. A browser's `nextId()` counter only knows its own
|
|
15546
|
+
* tab, so two tabs (or two people) would hand out the same id; a create whose
|
|
15547
|
+
* id is missing or already taken gets the next free one here instead.
|
|
15548
|
+
*/
|
|
15549
|
+
function serverStoreFile(entries) {
|
|
15550
|
+
return {
|
|
15551
|
+
path: "src/lib/server/store.ts",
|
|
15552
|
+
description: "In-memory rows, held once on the server (seeded on start, reset on restart).",
|
|
15553
|
+
contents: `/**
|
|
15554
|
+
* The app's in-memory data, held once per server process and reached from the
|
|
15555
|
+
* browser through each entity's /api route. Seeded on start, reset on restart:
|
|
15556
|
+
* bind an entity to SQL in the Studio to keep its rows.
|
|
15557
|
+
*/
|
|
15558
|
+
import { createInMemoryDataSource } from '@svgrid/enterprise'
|
|
15559
|
+
import type { EntitySchema } from '@svgrid/enterprise'
|
|
15560
|
+
import { ${entries.map((e) => namesFor(e.schema).schemaVar).join(", ")}, ${entries.map((e) => `type ${namesFor(e.schema).type}`).join(", ")} } from '$lib/schemas'
|
|
15561
|
+
|
|
15562
|
+
/** An in-memory source that assigns a free id when a create brings none, or a
|
|
15563
|
+
* taken one. \`prefix\` null means the id is numeric (max + 1). */
|
|
15564
|
+
function serverHeld<T extends Record<string, unknown>>(rows: T[], schema: EntitySchema<T>, idField: string, prefix: string | null) {
|
|
15565
|
+
const source = createInMemoryDataSource<T>(rows, schema)
|
|
15566
|
+
let seq = rows.length
|
|
15567
|
+
const freeId = (wanted: unknown): string | number => {
|
|
15568
|
+
const taken = new Set(source.rows().map((r) => String(r[idField])))
|
|
15569
|
+
if (wanted != null && wanted !== '' && !taken.has(String(wanted))) return wanted as string | number
|
|
15570
|
+
if (prefix === null) return Math.max(0, ...source.rows().map((r) => Number(r[idField]) || 0)) + 1
|
|
15571
|
+
let id = prefix + ++seq
|
|
15572
|
+
while (taken.has(id)) id = prefix + ++seq
|
|
15573
|
+
return id
|
|
15574
|
+
}
|
|
15575
|
+
return {
|
|
15576
|
+
...source,
|
|
15577
|
+
createRow: (input: Partial<T>) => source.createRow({ ...input, [idField]: freeId(input[idField]) } as Partial<T>),
|
|
15578
|
+
}
|
|
15579
|
+
}
|
|
15580
|
+
|
|
15581
|
+
${entries.map(({ schema, rows }) => {
|
|
15582
|
+
const n = namesFor(schema);
|
|
15583
|
+
const idField = resolveIdField(schema);
|
|
15584
|
+
const numeric = schema.fields.find((f) => f.field === idField)?.type === "number";
|
|
15585
|
+
return `export const ${storeVar(schema)} = serverHeld<${n.type}>(${JSON.stringify(rows)}, ${n.schemaVar}, ${JSON.stringify(idField)}, ${numeric ? "null" : JSON.stringify(n.idPrefix)})`;
|
|
15586
|
+
}).join("\n")}
|
|
15587
|
+
`
|
|
15588
|
+
};
|
|
15589
|
+
}
|
|
15590
|
+
/** The `createKitHandlers` call every entity route shares, plus the imports it needs. */
|
|
15591
|
+
function kitHandlersBlock(schema, feat) {
|
|
15592
|
+
const accessImport = feat.access ? `\nimport { authorizeAction, getServerRole } from '$lib/access'` : "";
|
|
15593
|
+
const auditImport = feat.audit ? `\nimport { recordAudit } from '$lib/audit'` : "";
|
|
15594
|
+
const tenantImport = feat.tenantField ? `\nimport { requireTenant } from '$lib/server/tenant'` : "";
|
|
15595
|
+
const opts = [
|
|
15596
|
+
`schema: ${namesFor(schema).schemaVar}`,
|
|
15597
|
+
`source`,
|
|
15598
|
+
`validate: true`
|
|
15599
|
+
];
|
|
15600
|
+
if (feat.access) opts.push(`// Server-enforced RBAC: the caller's role comes from the session (event.locals). Reads\n // are allowed only if the role can open one of this entity's own screens.\n authorize: ({ action, event }) => authorizeAction(getServerRole(event), action, ${JSON.stringify(feat.screenIds ?? [])})`);
|
|
15601
|
+
if (feat.tenantField) opts.push(`// Multi-tenancy, enforced server-side: reads are filtered to the caller's tenant,\n // creates are stamped with it, and update/delete re-read the target under the\n // scope first - so a guessed id cannot reach another tenant's row.\n scope: ({ event }) => ({ field: ${JSON.stringify(feat.tenantField)}, value: requireTenant(event) })`);
|
|
15602
|
+
if (feat.audit) opts.push(`// Record every successful write to the audit trail.\n audit: (e) => recordAudit({ entity: ${JSON.stringify(schema.name)}, action: e.action, recordId: e.id, values: e.values as Record<string, unknown> | undefined, actor: String(e.event.locals?.role ?? e.event.locals?.user ?? 'system') })`);
|
|
15603
|
+
const hooks = triggerHooksOpt(feat.triggers);
|
|
15604
|
+
if (hooks) opts.push(`// Business rules, enforced server-side (a client that skips them still can't write bad data).\n ${hooks}`);
|
|
15605
|
+
const handlers = `export const { POST } = createKitHandlers({\n ${opts.join(",\n ")},\n})`;
|
|
15606
|
+
return {
|
|
15607
|
+
imports: `${accessImport}${auditImport}${tenantImport}`,
|
|
15608
|
+
handlers
|
|
15609
|
+
};
|
|
15610
|
+
}
|
|
15334
15611
|
/** Map an EntityField type to a Postgres column type for PGlite DDL. */
|
|
15335
15612
|
var PG_COL_TYPE = {
|
|
15336
15613
|
number: "double precision",
|
|
@@ -15469,7 +15746,7 @@ function sqlDdlFiles(schemas, sources) {
|
|
|
15469
15746
|
contents: header("Sample seed rows (optional)") + inserts.join("\n\n") + "\n"
|
|
15470
15747
|
}] : []];
|
|
15471
15748
|
}
|
|
15472
|
-
function dataModule(entries, seed, sources, supabaseConn) {
|
|
15749
|
+
function dataModule(entries, seed, sources, supabaseConn, serverMemory = false) {
|
|
15473
15750
|
const schemas = entries.map((e) => e.schema);
|
|
15474
15751
|
const typeImports = schemas.map((s) => `type ${pascal(s.name)}`).join(", ");
|
|
15475
15752
|
const schemaImports = schemas.map((s) => namesFor(s).schemaVar).join(", ");
|
|
@@ -15477,7 +15754,8 @@ function dataModule(entries, seed, sources, supabaseConn) {
|
|
|
15477
15754
|
const entImports = /* @__PURE__ */ new Set();
|
|
15478
15755
|
const needs = {
|
|
15479
15756
|
supabase: false,
|
|
15480
|
-
sqlRoutes: []
|
|
15757
|
+
sqlRoutes: [],
|
|
15758
|
+
memoryRoutes: []
|
|
15481
15759
|
};
|
|
15482
15760
|
const stores = entries.map((e) => {
|
|
15483
15761
|
const T = pascal(e.schema.name);
|
|
@@ -15517,9 +15795,17 @@ function dataModule(entries, seed, sources, supabaseConn) {
|
|
|
15517
15795
|
});
|
|
15518
15796
|
return `const ${store} = createSqlDataSource<${T}>({ schema: ${sv}, table: ${sq(src.table)}, dialect: { placeholders: '$', ilike: true }, execute: async (text, params) => { await pgReady; return (await pg.query(text, params)).rows as Record<string, unknown>[] } })`;
|
|
15519
15797
|
default: {
|
|
15798
|
+
const rowList = (src.kind === "memory" ? src.seed : void 0) ?? seed.get(e.schema.name) ?? [];
|
|
15799
|
+
if (serverMemory) {
|
|
15800
|
+
entImports.add("createKitDataSource");
|
|
15801
|
+
needs.memoryRoutes.push({
|
|
15802
|
+
schema: e.schema,
|
|
15803
|
+
rows: rowList
|
|
15804
|
+
});
|
|
15805
|
+
return `const ${store} = createKitDataSource<${T}>({ endpoint: '/api/${namesFor(e.schema).route}' })`;
|
|
15806
|
+
}
|
|
15520
15807
|
entImports.add("createInMemoryDataSource");
|
|
15521
|
-
const
|
|
15522
|
-
return `const ${store} = createInMemoryDataSource<${T}>(${JSON.stringify(curated ?? seed.get(e.schema.name) ?? [])}, ${sv})`;
|
|
15808
|
+
return `const ${store} = createInMemoryDataSource<${T}>(${JSON.stringify(rowList)}, ${sv})`;
|
|
15523
15809
|
}
|
|
15524
15810
|
}
|
|
15525
15811
|
}).join("\n");
|
|
@@ -15983,7 +16269,7 @@ function prepareEntities(schemas) {
|
|
|
15983
16269
|
/** Emit the shared entity modules: `src/lib/schemas.ts` + `src/lib/data.ts` (+ `connections.ts`). */
|
|
15984
16270
|
function emitEntityModules(schemas, opts = {}) {
|
|
15985
16271
|
const { entries, seed } = prepareEntities(schemas);
|
|
15986
|
-
const { file: data, needs } = dataModule(entries, seed, opts.sources, opts.supabaseConn);
|
|
16272
|
+
const { file: data, needs } = dataModule(entries, seed, opts.sources, opts.supabaseConn, opts.serverMemory);
|
|
15987
16273
|
const files = [schemasModule(entries), data];
|
|
15988
16274
|
if ((needs.supabase || opts.supabaseAuth) && !needs.supabaseUrl && (opts.supabaseConn?.url || opts.supabaseConn?.key)) {
|
|
15989
16275
|
needs.supabaseUrl = opts.supabaseConn.url;
|
|
@@ -15998,6 +16284,16 @@ function emitEntityModules(schemas, opts = {}) {
|
|
|
15998
16284
|
dbSchema: r.dbSchema,
|
|
15999
16285
|
tenantField: opts.tenantField && opts.tenantScoped?.(r.schema.name) !== false ? opts.tenantField : void 0
|
|
16000
16286
|
}));
|
|
16287
|
+
if (needs.memoryRoutes.length) {
|
|
16288
|
+
files.push(serverStoreFile(needs.memoryRoutes));
|
|
16289
|
+
for (const r of needs.memoryRoutes) files.push(memoryRouteFile(r.schema, {
|
|
16290
|
+
access: opts.accessEnabled,
|
|
16291
|
+
audit: opts.auditEnabled,
|
|
16292
|
+
screenIds: opts.screensByEntity?.get(r.schema.name) ?? [],
|
|
16293
|
+
triggers: opts.triggers?.[r.schema.name],
|
|
16294
|
+
tenantField: opts.tenantField && opts.tenantScoped?.(r.schema.name) !== false ? opts.tenantField : void 0
|
|
16295
|
+
}));
|
|
16296
|
+
}
|
|
16001
16297
|
return {
|
|
16002
16298
|
files,
|
|
16003
16299
|
prepared: entries.map((e) => e.schema)
|
|
@@ -16045,7 +16341,7 @@ function hasFieldConditions(schema) {
|
|
|
16045
16341
|
*
|
|
16046
16342
|
* `version.test.ts` asserts this matches `package.json`, so the two cannot drift.
|
|
16047
16343
|
*/
|
|
16048
|
-
var SVGRID_VERSION = "3.1.
|
|
16344
|
+
var SVGRID_VERSION = "3.1.5";
|
|
16049
16345
|
/**
|
|
16050
16346
|
* The version of `@svgrid/grid`, which releases on its own line: enterprise is
|
|
16051
16347
|
* on 3.1.x while the grid is on 3.0.x.
|
|
@@ -16058,7 +16354,7 @@ var SVGRID_VERSION = "3.1.4";
|
|
|
16058
16354
|
*
|
|
16059
16355
|
* `version.test.ts` asserts this matches packages/grid/package.json.
|
|
16060
16356
|
*/
|
|
16061
|
-
var GRID_VERSION = "3.0.
|
|
16357
|
+
var GRID_VERSION = "3.0.9";
|
|
16062
16358
|
//#endregion
|
|
16063
16359
|
//#region src/studio/emit-project.ts
|
|
16064
16360
|
var has = (blocks, kind) => blocks.some((b) => b.config.kind === kind);
|
|
@@ -18048,27 +18344,28 @@ function screenChildEntities(blocks, resolve) {
|
|
|
18048
18344
|
}
|
|
18049
18345
|
return [...out.values()];
|
|
18050
18346
|
}
|
|
18051
|
-
function ssrReadServerFile(schema, screen,
|
|
18052
|
-
const n = namesFor(schema);
|
|
18053
|
-
const isSql = sourceKind === "sql";
|
|
18347
|
+
function ssrReadServerFile(schema, screen, routed, accessEnabled, screenIds, resolve) {
|
|
18054
18348
|
const children = screenChildEntities(screen.blocks, resolve);
|
|
18055
|
-
const
|
|
18056
|
-
const
|
|
18057
|
-
const
|
|
18349
|
+
const viaRoute = routed(schema.name);
|
|
18350
|
+
const rbac = accessEnabled && !viaRoute;
|
|
18351
|
+
const srcExpr = (s) => routed(s.name) ? `createKitDataSource<Record<string, unknown>>({ endpoint: ${jsStr("/api/" + namesFor(s).route)}, fetch })` : namesFor(s).sourceVar;
|
|
18352
|
+
const all = [schema, ...children];
|
|
18353
|
+
const anyRouted = all.some((s) => routed(s.name));
|
|
18354
|
+
const dataImports = [...new Set(all.filter((s) => !routed(s.name)).map((s) => namesFor(s).sourceVar))];
|
|
18058
18355
|
const childLoads = children.map((c) => ` const ${mdChildVar(c.name)} = (await ${srcExpr(c)}.getRows(PAGE)).rows`).join("\n");
|
|
18059
18356
|
return {
|
|
18060
18357
|
path: `src/routes/${screen.route}/+page.server.ts`,
|
|
18061
18358
|
description: `${screen.title} - SSR load (full dataset${children.length ? " + child collections" : ""}).`,
|
|
18062
18359
|
contents: `import type { PageServerLoad } from './$types'
|
|
18063
18360
|
import type { ServerRequest } from '@svgrid/grid'
|
|
18064
|
-
${
|
|
18361
|
+
${anyRouted ? "import { createKitDataSource } from '@svgrid/enterprise'\n" : ""}${rbac ? "import { error } from '@sveltejs/kit'\nimport { authorizeAction, getServerRole } from '$lib/access'\n" : ""}${dataImports.length ? `import { ${dataImports.join(", ")} } from '$lib/data'\n` : ""}
|
|
18065
18362
|
// This screen renders on the server: real SSR HTML, not just a server-side
|
|
18066
18363
|
// load. Stated explicitly so it holds whichever way the root layout is set.
|
|
18067
18364
|
export const ssr = true
|
|
18068
18365
|
${rbac ? `\nconst SCREEN_IDS = ${JSON.stringify(screenIds)}\n` : ""}
|
|
18069
18366
|
const PAGE: ServerRequest = { startRow: 0, endRow: 1000, pageIndex: 0, pageSize: 1000, sortModel: [], filterModel: {} }
|
|
18070
18367
|
|
|
18071
|
-
export const load: PageServerLoad = async (${
|
|
18368
|
+
export const load: PageServerLoad = async (${anyRouted || rbac ? `{ ${[anyRouted ? "fetch" : "", rbac ? "locals" : ""].filter(Boolean).join(", ")} }` : ""}) => {
|
|
18072
18369
|
${rbac ? ` if (!authorizeAction(getServerRole({ locals }), 'read', SCREEN_IDS)) throw error(403, 'Not allowed')\n` : ""} const rows = (await ${srcExpr(schema)}.getRows(PAGE)).rows
|
|
18073
18370
|
${childLoads ? childLoads + "\n" : ""} return { rows${children.map((c) => `, ${mdChildVar(c.name)}`).join("")} }
|
|
18074
18371
|
}
|
|
@@ -18135,10 +18432,9 @@ function attrs(f) {
|
|
|
18135
18432
|
return out.length ? " " + out.join(" ") : "";
|
|
18136
18433
|
}
|
|
18137
18434
|
/** The `+page.server.ts` + SSR `+page.svelte` for a single-grid CRUD screen. */
|
|
18138
|
-
function emitSsrGridScreen(schema, screen,
|
|
18435
|
+
function emitSsrGridScreen(schema, screen, routed, accessEnabled, screenIds, byName) {
|
|
18139
18436
|
const n = namesFor(schema);
|
|
18140
|
-
const isSql =
|
|
18141
|
-
const isRest = sourceKind === "rest";
|
|
18437
|
+
const isSql = routed(schema.name);
|
|
18142
18438
|
const relSchemaOf = (f) => byName.get(f.relation.entity);
|
|
18143
18439
|
const relIdOf = (rs) => rs.idField ?? rs.fields.find((x) => x.primaryKey)?.field ?? "id";
|
|
18144
18440
|
const grid = screen.blocks.find((b) => b.config.kind === "grid").config;
|
|
@@ -18153,30 +18449,32 @@ function emitSsrGridScreen(schema, screen, sourceKind, accessEnabled, screenIds,
|
|
|
18153
18449
|
const fieldTypesLit = `{ ${formFields.map((f) => `${jsStr(f.field)}: ${jsStr(normType(f.type))}`).join(", ")} }`;
|
|
18154
18450
|
const isRel = (f) => f.type === "relation" && !!f.relation && byName.has(f.relation.entity);
|
|
18155
18451
|
const relFields = formFields.filter(isRel);
|
|
18156
|
-
const
|
|
18452
|
+
const relRouted = relFields.some((f) => routed(relSchemaOf(f).name));
|
|
18453
|
+
const relSourceVars = [...new Set(relFields.filter((f) => !routed(relSchemaOf(f).name)).map((f) => namesFor(relSchemaOf(f)).sourceVar))];
|
|
18157
18454
|
const relPrefetch = relFields.map((f) => {
|
|
18158
18455
|
const rs = relSchemaOf(f);
|
|
18159
18456
|
const rel = namesFor(rs);
|
|
18160
18457
|
const relId = relIdOf(rs);
|
|
18161
18458
|
const labelF = f.relation.labelField ?? relId;
|
|
18162
|
-
const relSource =
|
|
18459
|
+
const relSource = routed(rs.name) ? `createKitDataSource<Record<string, unknown>>({ endpoint: ${jsStr("/api/" + rel.route)}, fetch })` : rel.sourceVar;
|
|
18163
18460
|
return ` const ${f.field}Options = (await ${relSource}.getRows({ startRow: 0, endRow: 100, pageIndex: 0, pageSize: 100, sortModel: [], filterModel: {} })).rows.map((r: Record<string, unknown>) => ({ value: String(r[${jsStr(relId)}] ?? ''), label: String(r[${jsStr(labelF)}] ?? '') }))`;
|
|
18164
18461
|
}).join("\n");
|
|
18165
18462
|
const relReturn = relFields.map((f) => `, ${f.field}Options`).join("");
|
|
18166
18463
|
const src = isSql ? "source(fetch)" : n.sourceVar;
|
|
18167
18464
|
const fetchArg = isSql ? ", fetch" : "";
|
|
18465
|
+
const loadFetchArg = isSql || relRouted ? ", fetch" : "";
|
|
18168
18466
|
const conditional = hasFieldConditions(schema);
|
|
18169
18467
|
const readValues = (fd) => conditional ? `stripHiddenValues(${n.schemaVar}, formToValues(${fd}))` : `formToValues(${fd})`;
|
|
18170
18468
|
const enterpriseImports = [
|
|
18171
18469
|
"validateAll",
|
|
18172
18470
|
...conditional ? ["stripHiddenValues"] : [],
|
|
18173
|
-
...isSql ? ["createKitDataSource"] : []
|
|
18471
|
+
...isSql || relRouted ? ["createKitDataSource"] : []
|
|
18174
18472
|
].join(", ");
|
|
18175
|
-
const
|
|
18473
|
+
const dataImports = [.../* @__PURE__ */ new Set([...isSql ? [] : [n.sourceVar], ...relSourceVars])];
|
|
18474
|
+
const sourceImport = dataImports.length ? `import { ${dataImports.join(", ")} } from '$lib/data'\n` : "";
|
|
18176
18475
|
const schemaImport = `import { ${n.schemaVar}${isSql ? `, type ${n.type}` : ""} } from '$lib/schemas'`;
|
|
18177
|
-
const idConst = isSql || isRest ? "" : `\nconst ID_FIELD = ${jsStr(idField)}`;
|
|
18178
18476
|
const srcHelper = isSql ? `\n// Same-origin client over the connected /api/${n.route} route (that route runs\n// validation, RBAC, triggers + audit via createKitHandlers).\nconst source = (fetch: typeof globalThis.fetch) => createKitDataSource<${n.type}>({ endpoint: ${jsStr("/api/" + n.route)}, fetch })\n` : "";
|
|
18179
|
-
const createCall =
|
|
18477
|
+
const createCall = `await ${src}.createRow(values)`;
|
|
18180
18478
|
const rbac = accessEnabled && !isSql;
|
|
18181
18479
|
const localsArg = rbac ? ", locals" : "";
|
|
18182
18480
|
const readGuard = rbac ? ` if (!authorizeAction(getServerRole({ locals }), 'read', SCREEN_IDS)) throw error(403, 'Not allowed')\n` : "";
|
|
@@ -18190,7 +18488,7 @@ import { planFromSearchParams } from '$lib/server/query'
|
|
|
18190
18488
|
// This screen renders on the server: real SSR HTML, not just a server-side
|
|
18191
18489
|
// load. Stated explicitly so it holds whichever way the root layout is set.
|
|
18192
18490
|
export const ssr = true
|
|
18193
|
-
${
|
|
18491
|
+
${rbac ? `\nconst SCREEN_IDS = ${JSON.stringify(screenIds)}` : ""}
|
|
18194
18492
|
const FIELD_TYPES: Record<string, 'text' | 'number' | 'boolean'> = ${fieldTypesLit}
|
|
18195
18493
|
${srcHelper}
|
|
18196
18494
|
/** Read a submitted form into a typed partial row. Booleans come from checkbox
|
|
@@ -18206,7 +18504,7 @@ function formToValues(fd: FormData): Record<string, unknown> {
|
|
|
18206
18504
|
return values
|
|
18207
18505
|
}
|
|
18208
18506
|
|
|
18209
|
-
export const load: PageServerLoad = async ({ url${
|
|
18507
|
+
export const load: PageServerLoad = async ({ url${loadFetchArg}${localsArg} }) => {
|
|
18210
18508
|
${readGuard} const plan = planFromSearchParams(url, ${pageSize})
|
|
18211
18509
|
const { rows, rowCount } = await ${src}.getRows(plan)
|
|
18212
18510
|
${relPrefetch ? relPrefetch + "\n" : ""} return { rows, total: rowCount, page: plan.pageIndex, size: plan.pageSize, sort: plan.sortModel${relReturn} }
|
|
@@ -18390,14 +18688,26 @@ ${g.title ? ` <legend>${htmlEsc(g.title)}</legend>\n` : ""}${inner}
|
|
|
18390
18688
|
const q = sp.toString()
|
|
18391
18689
|
void goto(q ? \`?\${q}\` : page.url.pathname, { keepFocus: true, noScroll: true })
|
|
18392
18690
|
}
|
|
18691
|
+
|
|
18692
|
+
// Only a sort the URL does not already hold navigates. Grid releases before
|
|
18693
|
+
// the mount-report fix also reported the starting sort when they mounted,
|
|
18694
|
+
// and acting on that would drop ?page on every load.
|
|
18695
|
+
function onSort(s: Array<{ id: string; desc: boolean }>) {
|
|
18696
|
+
const sort = s.map((x) => \`\${x.id}:\${x.desc ? 'desc' : 'asc'}\`).join(',')
|
|
18697
|
+
if (sort !== (page.url.searchParams.get('sort') ?? '')) setParams({ sort: sort || null, page: null })
|
|
18698
|
+
}
|
|
18393
18699
|
${wantsFilter ? `
|
|
18394
18700
|
// Filter via the URL (q = global search, f_<col> = per-column). The server's
|
|
18395
18701
|
// planFromSearchParams reads these, so load() re-filters; reset to the first page.
|
|
18396
18702
|
function applyFilters(f: { global: string; columns: Array<{ id: string; value: string }> }) {
|
|
18397
18703
|
const sp = new URLSearchParams(page.url.searchParams)
|
|
18704
|
+
const filterParams = (p: URLSearchParams) => [...p].filter(([k]) => k === 'q' || k.startsWith('f_')).sort().join('&')
|
|
18705
|
+
const before = filterParams(sp)
|
|
18398
18706
|
for (const k of [...sp.keys()]) if (k === 'q' || k.startsWith('f_')) sp.delete(k)
|
|
18399
18707
|
if (f.global) sp.set('q', f.global)
|
|
18400
18708
|
for (const c of f.columns) if (c.value) sp.set('f_' + c.id, c.value)
|
|
18709
|
+
// Nothing changed (older grid releases report the starting filters on mount).
|
|
18710
|
+
if (filterParams(sp) === before) return
|
|
18401
18711
|
sp.delete('page')
|
|
18402
18712
|
const q = sp.toString()
|
|
18403
18713
|
void goto(q ? \`?\${q}\` : page.url.pathname, { keepFocus: true, noScroll: true })
|
|
@@ -18431,12 +18741,13 @@ ${facetForm}
|
|
|
18431
18741
|
{columns}
|
|
18432
18742
|
externalSort
|
|
18433
18743
|
initialSorting={data.sort}
|
|
18744
|
+
showPagination
|
|
18434
18745
|
externalPagination${wantsFilter ? "\n filterable\n externalFilter\n onFiltersChange={applyFilters}" : ""}
|
|
18435
18746
|
rowCount={data.total}
|
|
18436
18747
|
pageIndex={data.page}
|
|
18437
18748
|
pageSize={data.size}
|
|
18438
|
-
onSortingChange={
|
|
18439
|
-
onPaginationChange={(p) => setParams({ page: String(p.pageIndex), size: String(p.pageSize) })}
|
|
18749
|
+
onSortingChange={onSort}
|
|
18750
|
+
onPaginationChange={(p) => { if (p.pageIndex !== data.page || p.pageSize !== data.size) setParams({ page: String(p.pageIndex), size: String(p.pageSize) }) }}
|
|
18440
18751
|
/>
|
|
18441
18752
|
|
|
18442
18753
|
{#if editing}
|
|
@@ -18502,7 +18813,8 @@ function emitStudioProject(project) {
|
|
|
18502
18813
|
const sources = Object.fromEntries(project.entities.map((e) => [e.name, entityDataSource(project, e.name)]));
|
|
18503
18814
|
const accessEnabled = project.access?.enabled === true && (project.access?.roles.length ?? 0) > 0;
|
|
18504
18815
|
const authEnabled = project.auth?.enabled === true;
|
|
18505
|
-
const
|
|
18816
|
+
const routed = (entity) => sources[entity]?.kind === "sql" || sources[entity]?.kind === "memory";
|
|
18817
|
+
const auditEnabled = project.audit === true && project.entities.some((e) => routed(e.name));
|
|
18506
18818
|
const i18nEnabled = project.i18n?.enabled === true && (project.i18n?.locales.length ?? 0) > 0;
|
|
18507
18819
|
const screensByEntity = /* @__PURE__ */ new Map();
|
|
18508
18820
|
for (const s of project.screens) {
|
|
@@ -18519,7 +18831,8 @@ function emitStudioProject(project) {
|
|
|
18519
18831
|
supabaseConn: project.supabase,
|
|
18520
18832
|
supabaseAuth: project.auth?.enabled === true && project.auth.provider === "supabase",
|
|
18521
18833
|
tenantField: tenantCol,
|
|
18522
|
-
tenantScoped: (name) => isTenantScoped(project, name)
|
|
18834
|
+
tenantScoped: (name) => isTenantScoped(project, name),
|
|
18835
|
+
serverMemory: true
|
|
18523
18836
|
});
|
|
18524
18837
|
const byName = new Map(prepared.map((s) => [s.name, s]));
|
|
18525
18838
|
const rawByName = new Map(project.entities.map((e) => [e.name, e]));
|
|
@@ -18549,10 +18862,8 @@ function emitStudioProject(project) {
|
|
|
18549
18862
|
const schema = byName.get(screen.entity);
|
|
18550
18863
|
if (!schema) throw new Error(`emitStudioProject: screen "${screen.title}" references missing entity "${screen.entity}"`);
|
|
18551
18864
|
if (isSsrScreen(project, screen)) {
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
if (ssrScreenShape(project, screen) === "grid") pages.push(...emitSsrGridScreen(schema, screen, srcKind, accessEnabled, screensByEntity.get(screen.entity) ?? [], byName));
|
|
18555
|
-
else pages.push(ssrReadServerFile(schema, screen, srcKind, accessEnabled, screensByEntity.get(screen.entity) ?? [], resolve), screenPage(schema, rawByName.get(screen.entity) ?? schema, screen, resolve, (name) => rawByName.get(name), accessEnabled, i18nEnabled, routeById, drillEnabled, true));
|
|
18865
|
+
if (ssrScreenShape(project, screen) === "grid") pages.push(...emitSsrGridScreen(schema, screen, routed, accessEnabled, screensByEntity.get(screen.entity) ?? [], byName));
|
|
18866
|
+
else pages.push(ssrReadServerFile(schema, screen, routed, accessEnabled, screensByEntity.get(screen.entity) ?? [], resolve), screenPage(schema, rawByName.get(screen.entity) ?? schema, screen, resolve, (name) => rawByName.get(name), accessEnabled, i18nEnabled, routeById, drillEnabled, true));
|
|
18556
18867
|
continue;
|
|
18557
18868
|
}
|
|
18558
18869
|
pages.push(screenPage(schema, rawByName.get(screen.entity) ?? schema, screen, resolve, (name) => rawByName.get(name), accessEnabled, i18nEnabled, routeById, drillEnabled, false, sources[screen.entity]));
|
|
@@ -18912,6 +19223,8 @@ export function requireTenant(event: TenantEvent): string {
|
|
|
18912
19223
|
* route checks `CRON_SECRET` and runs the due handlers.
|
|
18913
19224
|
*/
|
|
18914
19225
|
function jobsFiles(project, jobs, emailAvailable) {
|
|
19226
|
+
const isServerHeld = (ent) => entityDataSource(project, ent.name).kind === "memory";
|
|
19227
|
+
const jobSourceVar = (ent) => isServerHeld(ent) ? storeVar(ent) : namesFor(ent).sourceVar;
|
|
18915
19228
|
const handler = (j) => {
|
|
18916
19229
|
const label = jsStr(j.name);
|
|
18917
19230
|
if (j.kind === "email") {
|
|
@@ -18922,9 +19235,8 @@ function jobsFiles(project, jobs, emailAvailable) {
|
|
|
18922
19235
|
const why = !emailAvailable ? "email is not enabled on this project" : !j.to ? "no recipient set" : `unknown entity ${j.entity}`;
|
|
18923
19236
|
return ` ${JSON.stringify(j.id)}: async () => {\n // ${label}: cannot send - ${why}.\n console.warn('cron ${j.id}: skipped (${why})')\n },`;
|
|
18924
19237
|
}
|
|
18925
|
-
const n = namesFor(ent);
|
|
18926
19238
|
return ` ${JSON.stringify(j.id)}: async () => {
|
|
18927
|
-
const { rows, rowCount } = await ${
|
|
19239
|
+
const { rows, rowCount } = await ${jobSourceVar(ent)}.getRows({ startRow: 0, endRow: 10, sortModel: [], filterModel: {} })
|
|
18928
19240
|
const items = rows.map((r) => '<li>' + Object.values(r).slice(0, 3).map(String).join(' · ') + '</li>').join('')
|
|
18929
19241
|
await sendEmail(${to}, ${subject}, '<p>' + rowCount + ' ${ent.name} total. Most recent:</p><ul>' + items + '</ul>')
|
|
18930
19242
|
},`;
|
|
@@ -18933,19 +19245,26 @@ function jobsFiles(project, jobs, emailAvailable) {
|
|
|
18933
19245
|
return ` ${JSON.stringify(j.id)}: async () => {\n${body.split("\n").map((l) => " " + l).join("\n")}\n },`;
|
|
18934
19246
|
};
|
|
18935
19247
|
const entityImports = /* @__PURE__ */ new Set();
|
|
19248
|
+
const storeImports = /* @__PURE__ */ new Set();
|
|
18936
19249
|
for (const j of jobs) {
|
|
18937
19250
|
if (j.kind !== "email") continue;
|
|
18938
19251
|
const ent = project.entities.find((e) => e.name === j.entity);
|
|
18939
|
-
if (ent
|
|
19252
|
+
if (!ent || !emailAvailable || !j.to) continue;
|
|
19253
|
+
if (isServerHeld(ent)) storeImports.add(jobSourceVar(ent));
|
|
19254
|
+
else entityImports.add(jobSourceVar(ent));
|
|
18940
19255
|
}
|
|
18941
|
-
const imports = [
|
|
19256
|
+
const imports = [
|
|
19257
|
+
...entityImports.size ? [`import { ${[...entityImports].sort().join(", ")} } from '$lib/data'`] : [],
|
|
19258
|
+
...storeImports.size ? [`import { ${[...storeImports].sort().join(", ")} } from '$lib/server/store'`] : [],
|
|
19259
|
+
...jobs.some((j) => j.kind === "email") && emailAvailable ? [`import { sendEmail } from '$lib/server/email'`] : []
|
|
19260
|
+
];
|
|
18942
19261
|
return [{
|
|
18943
19262
|
path: "src/lib/server/jobs.ts",
|
|
18944
19263
|
description: "Scheduled job handlers, keyed by job id.",
|
|
18945
19264
|
contents: `// Regenerated by SvGrid Studio. Scheduled job handlers.
|
|
18946
19265
|
//
|
|
18947
19266
|
// Schedule (UTC):
|
|
18948
|
-
${jobs.map((j) =>
|
|
19267
|
+
${jobs.map((j) => `// ${j.id.padEnd(20)} ${j.cron.padEnd(16)} ${j.name}`).join("\n")}
|
|
18949
19268
|
//
|
|
18950
19269
|
// Runs on the server, triggered by /api/cron - NOT in the browser. Edit a
|
|
18951
19270
|
// handler body freely; the registry keys are what /api/cron dispatches on.
|
|
@@ -20548,8 +20867,8 @@ function rootLayoutModule(project) {
|
|
|
20548
20867
|
const anySsr = project.screens.some((s) => isSsrScreen(project, s));
|
|
20549
20868
|
return {
|
|
20550
20869
|
path: "src/routes/+layout.ts",
|
|
20551
|
-
description: anySsr ? "Root layout options (SSR on; client-only screens opt out per page)." : "Client SPA (
|
|
20552
|
-
contents: anySsr ? `// Server rendering stays on (SvelteKit's default). Screens that have to run in\n// the browser -
|
|
20870
|
+
description: anySsr ? "Root layout options (SSR on; client-only screens opt out per page)." : "Client SPA (no screen renders on the server).",
|
|
20871
|
+
contents: anySsr ? `// Server rendering stays on (SvelteKit's default). Screens that have to run in\n// the browser - a shape that can't render on the server, or a source the server\n// can't reach - opt out in their own +page.ts.\nexport const prerender = false\n` : `// No screen here renders on the server, so the app runs as a client SPA.\nexport const ssr = false\nexport const prerender = false\n`
|
|
20553
20872
|
};
|
|
20554
20873
|
}
|
|
20555
20874
|
/** The static SvelteKit + Vite scaffolding around the generated screens. */
|
|
@@ -21099,7 +21418,7 @@ function emitStudioAppBundle(project) {
|
|
|
21099
21418
|
const plan = deployPlan(project);
|
|
21100
21419
|
const deploySteps = plan.dashboard ? `1. Push this folder to a Git repo (GitHub / GitLab / Bitbucket).\n2. Import it at <${plan.dashboard}> - build settings are detected automatically.\n\nOr deploy straight from your machine with the CLI:\n\n\`\`\`bash\n${plan.cli}\n\`\`\`` : `Build and run the server:\n\n\`\`\`bash\n${plan.cli}\n\`\`\``;
|
|
21101
21420
|
const readme = `# ${project.title}\n\nGenerated with SvGrid Studio.\n\n\`\`\`bash\nnpm install\nnpm run dev\n\`\`\`\n\nSQL / Supabase entities read their connection from \`.env\` (\`DATABASE_URL\`) or\n\`src/lib/connections.ts\`. Everything else runs on seeded in-memory data.\n\n## Deploy (${plan.label})\n\nThis app is configured for **${plan.label}** (SvelteKit \`${plan.adapterModule}\`).\n\n${deploySteps}\n\nTo target a different host, pick another **Deploy target** in the designer and\nre-generate, or swap the adapter in \`svelte.config.js\`. Entities on **Local\ndatabase** (PGlite) or **In-memory** need no server env; SQL / Supabase entities\nneed their connection set in the host's environment variables.\n\n## Round-tripping back into the designer\n\nThis app ships its own design model in \`studio.config.json\`. To keep editing\nvisually, open the SvGrid Studio designer and **Load** that file - your entities,\nscreens, blocks, theme, RBAC, i18n, etc. come back exactly as generated, and you\ncan re-generate from there.\n\nThe designer regenerates the files under \`src/routes\` and \`src/lib\` from the\nmodel, so **keep your own custom code in new files/modules and import it**, rather\nthan editing the generated screens in place - that way a re-generate never\nclobbers your work. (The CLI workflow, \`npx @svgrid/studio add\`, is the\nalternative: it wraps generated code in \`svgrid:managed\` markers and preserves\nanything you write outside them.)\n`;
|
|
21102
|
-
|
|
21421
|
+
const bundle = [...[
|
|
21103
21422
|
{
|
|
21104
21423
|
path: "package.json",
|
|
21105
21424
|
description: "Dependencies + scripts (npm install, npm run dev).",
|
|
@@ -21173,6 +21492,7 @@ function emitStudioAppBundle(project) {
|
|
|
21173
21492
|
})
|
|
21174
21493
|
}
|
|
21175
21494
|
], ...generated];
|
|
21495
|
+
return project.kit === 3 ? toSvelteKit3(bundle) : bundle;
|
|
21176
21496
|
}
|
|
21177
21497
|
/**
|
|
21178
21498
|
* Emit ONLY the app content (`src/routes` + `src/lib` + `db`) to drop into an
|
|
@@ -33241,7 +33561,7 @@ var restaurant = {
|
|
|
33241
33561
|
order: 2
|
|
33242
33562
|
}, {
|
|
33243
33563
|
startField: "reservationTime",
|
|
33244
|
-
titleField: "
|
|
33564
|
+
titleField: "customerPhone",
|
|
33245
33565
|
colorField: "status",
|
|
33246
33566
|
resourceField: "tableId",
|
|
33247
33567
|
initialView: "day"
|
|
@@ -41501,7 +41821,7 @@ async function runStudioInit(flags, prompts, db, io, fetchText = null) {
|
|
|
41501
41821
|
const title = flags.yes || flags.title ? defaultTitle : (await prompts.ask("\nApp name:", defaultTitle)).trim() || defaultTitle;
|
|
41502
41822
|
const outDir = flags.out ?? (flags.yes ? "." : (await prompts.ask("Write it where?", ".")).trim() || ".");
|
|
41503
41823
|
await confirmTargetFolder(outDir, flags, prompts, io, outDir === candidateDir ? markersBefore : null);
|
|
41504
|
-
const
|
|
41824
|
+
const built = crudAppFromSchemas(data.schemas, {
|
|
41505
41825
|
title,
|
|
41506
41826
|
dataSource: data.kind,
|
|
41507
41827
|
sources: data.sources,
|
|
@@ -41510,6 +41830,7 @@ async function runStudioInit(flags, prompts, db, io, fetchText = null) {
|
|
|
41510
41830
|
overviewDashboard: true,
|
|
41511
41831
|
...theme ? { theme } : {}
|
|
41512
41832
|
});
|
|
41833
|
+
const project = flags.kit === 3 ? setKitVersion(built, 3) : built;
|
|
41513
41834
|
const written = await writeAll(emitStudioAppBundle(project), outDir, io);
|
|
41514
41835
|
const configPath = inDir(outDir, "studio.config.json");
|
|
41515
41836
|
await io.writeFile(configPath, serializeProject(project));
|
|
@@ -41967,4 +42288,4 @@ function checkLicenseKey(key, now = /* @__PURE__ */ new Date()) {
|
|
|
41967
42288
|
};
|
|
41968
42289
|
}
|
|
41969
42290
|
//#endregion
|
|
41970
|
-
export { CANVAS_COLS, CANVAS_GAP_PX, CANVAS_ROW_PX, CONTAINER_CHILD_KINDS, CONTAINER_DISPLAY_KINDS, CRUD_ACTIONS, DEFAULT_PORT, DRIVER_PACKAGE, FORM_SUBMIT, GRID_API_MEMBERS, GRID_CURATED_PROPS, GRID_EVENTS, GRID_PRESETS, MANAGED_END, MANAGED_START, ON_LOAD, PANE_LAYOUTS, STANDARD_UI_EVENTS, TAB_CHILD_KINDS, TRIGGER_EVENTS, UI_COMPONENT_REGISTRY, UserError, addAccordionBlock, addAccordionComponent, addAccordionSection, addBlock, addBlockAt, addComponentBlock, addCrudSuite, addEntity, addFormSection, addFreestandingScreen, addHandlerStep, addScreen, addScreenAction, addScreenFromTemplate, addStateVar, addTab, addTabBlock, addTabComponent, applyGridPreset, blockClassName, blockColumns, blockPalette, blockStyleCss, buildCanvasLayout, buildConnectionString, buildCopilotMessages, buildDockLayout, buildStudioBugReport, canvasOpts, canvasRectOf, changeSlot, checkLicenseKey, clickSlot, compileCondition, compileHandlerSteps, compileStep, compileTriggerStep, compileTriggerSteps, compileValue, componentHandleMembers, componentHandleName, componentHasBindings, countTableRows, createProject, crudAppFromSchemas, crudSuiteScreens, csvToEntity, ctxAmbientDts, ctxCompletions, dashScreen, defaultBlockConfig, defaultEntitySource, defaultScreenFor, defaultStudioTheme, deployCommands, detailScreen, detectDelimiter, disableScreenCode, dockOpts, dockPaneIds, dockPaneTitleOf, duplicateBlock, duplicateScreen, emitEntityModules, emitStudioApp, emitStudioAppBundle, emitStudioFragment, emitStudioProject, enableScreenCode, entityDataSource, entityOf, entityScreenPage, eventSlot, flattenBlocks, formControlSettings, formControlsFor, formPlan, formScreen, generateRows, generateValue, getLiveDataSample, getSampleApp, getStarterDataset, getStudioTheme, gridApiSettableProps, gridColumns, gridConfig, gridIconGroups, gridOpts, gridPresetLabel, gridPropSurface, inferType, insertBlock, introspectDatabase, introspectDrizzle, introspectDrizzleAll, introspectJson, introspectOpenApi, introspectPrisma, introspectPrismaAll, isDarkTheme, isFieldHidden, isFileDialect, isPaneLayout, isProjectValid, isTenantScoped, isUserError, linkRelationLabels, listDatabaseTables, listScreen, liveDataSamples, mapSqlType, mergeBlockStyle, mergeManaged, missingEnvKeys, moveBlock, moveFormField, moveFormFields, moveFormSection, moveHandlerStep, parseConnectionString, parseCsv, parseProject, parseSelection, pickLabelField, prepareEntities, probeConnection, projectFromModelText, reconcileDock, redactConnectionString, refineField, refineFields, removeAccordionBlock, removeAccordionSection, removeBlock, removeEntity, removeFormSection, removeScreen, removeScreenAction, removeStateVar, removeTab, removeTabBlock, renameAccordionSection, renameTab, reorderBlock, reorderScreen, reseedScreenDock, resolveDeployTarget, resolveIdField, resolveSchema, resolveSchemas, resolveThemeTokens, resolveThemeTokensFor, roleCanAction, roleCanScreen, rowSelectSlot, runStudioAdd, runStudioAddApp, runStudioInit, runtimeDeps, sampleApps, sanitizeClassName, sanitizeProject, sanitizeStudioProject, scaffold, scaffoldApp, schemaToColumns, schemaToFormFields, screenDataset, screenFromTemplate, screenHandles, screenLayoutOf, seedUsers, serializeProject, setAccordionMultiple, setAuth, setCanvasRect, setComponentBinding, setComponentName, setDataLayer, setDataSource, setDeployTarget, setDockPaneTitle, setEntityDataSource, setEntityForm, setFieldConditions, setFieldHidden, setFieldInput, setFormColumns, setHandlerBody, setHandlerSteps, setJob, setLayoutOpts, setScreenDock, setScreenHandlersSource, setScreenLayout, setScreenRenderGrid, setScreenRenderMode, setShell, setTenancy, setTheme, setThemePreset, setTrigger, skipUserOwned, splitOpts, ssrEligible, ssrScreenShape, stackOpts, starterDatasets, starterProject, stateInitExpr, stateTsType, statusPills, stepsToCode, studioDeployInfo, studioThemes, suggestFormSections, summarizeVerify, syncDockPanes, tenantField, themeStyleString, triggersOf, uiComponentSpec, updateBlock, updateEntity, updateEntityField, updateFormSection, updateHandlerStep, updateScreen, updateStateVar, validateProject, verifyScaffold, withSsrDefaults };
|
|
42291
|
+
export { CANVAS_COLS, CANVAS_GAP_PX, CANVAS_ROW_PX, CONTAINER_CHILD_KINDS, CONTAINER_DISPLAY_KINDS, CRUD_ACTIONS, DEFAULT_PORT, DRIVER_PACKAGE, FORM_SUBMIT, GRID_API_MEMBERS, GRID_CURATED_PROPS, GRID_EVENTS, GRID_PRESETS, MANAGED_END, MANAGED_START, ON_LOAD, PANE_LAYOUTS, STANDARD_UI_EVENTS, SVELTEKIT_3_ADAPTERS, SVELTEKIT_3_DEV_DEPENDENCIES, TAB_CHILD_KINDS, TRIGGER_EVENTS, UI_COMPONENT_REGISTRY, UserError, addAccordionBlock, addAccordionComponent, addAccordionSection, addBlock, addBlockAt, addComponentBlock, addCrudSuite, addEntity, addFormSection, addFreestandingScreen, addHandlerStep, addScreen, addScreenAction, addScreenFromTemplate, addStateVar, addTab, addTabBlock, addTabComponent, applyGridPreset, blockClassName, blockColumns, blockPalette, blockStyleCss, buildCanvasLayout, buildConnectionString, buildCopilotMessages, buildDockLayout, buildStudioBugReport, canvasOpts, canvasRectOf, changeSlot, checkLicenseKey, clickSlot, compileCondition, compileHandlerSteps, compileStep, compileTriggerStep, compileTriggerSteps, compileValue, componentHandleMembers, componentHandleName, componentHasBindings, countTableRows, createProject, crudAppFromSchemas, crudSuiteScreens, csvToEntity, ctxAmbientDts, ctxCompletions, dashScreen, defaultBlockConfig, defaultEntitySource, defaultScreenFor, defaultStudioTheme, deployCommands, detailScreen, detectDelimiter, disableScreenCode, dockOpts, dockPaneIds, dockPaneTitleOf, duplicateBlock, duplicateScreen, emitEntityModules, emitStudioApp, emitStudioAppBundle, emitStudioFragment, emitStudioProject, enableScreenCode, entityDataSource, entityOf, entityScreenPage, eventSlot, flattenBlocks, formControlSettings, formControlsFor, formPlan, formScreen, generateRows, generateValue, getLiveDataSample, getSampleApp, getStarterDataset, getStudioTheme, gridApiSettableProps, gridColumns, gridConfig, gridIconGroups, gridOpts, gridPresetLabel, gridPropSurface, inferType, insertBlock, introspectDatabase, introspectDrizzle, introspectDrizzleAll, introspectJson, introspectOpenApi, introspectPrisma, introspectPrismaAll, isDarkTheme, isFieldHidden, isFileDialect, isPaneLayout, isProjectValid, isTenantScoped, isUserError, linkRelationLabels, listDatabaseTables, listScreen, liveDataSamples, mapSqlType, mergeBlockStyle, mergeManaged, missingEnvKeys, moveBlock, moveFormField, moveFormFields, moveFormSection, moveHandlerStep, parseConnectionString, parseCsv, parseProject, parseSelection, pickLabelField, prepareEntities, probeConnection, projectFromModelText, reconcileDock, redactConnectionString, refineField, refineFields, removeAccordionBlock, removeAccordionSection, removeBlock, removeEntity, removeFormSection, removeScreen, removeScreenAction, removeStateVar, removeTab, removeTabBlock, renameAccordionSection, renameTab, reorderBlock, reorderScreen, reseedScreenDock, resolveDeployTarget, resolveIdField, resolveSchema, resolveSchemas, resolveThemeTokens, resolveThemeTokensFor, roleCanAction, roleCanScreen, rowSelectSlot, runStudioAdd, runStudioAddApp, runStudioInit, runtimeDeps, sampleApps, sanitizeClassName, sanitizeProject, sanitizeStudioProject, scaffold, scaffoldApp, schemaToColumns, schemaToFormFields, screenDataset, screenFromTemplate, screenHandles, screenLayoutOf, seedUsers, serializeProject, setAccordionMultiple, setAuth, setCanvasRect, setComponentBinding, setComponentName, setDataLayer, setDataSource, setDeployTarget, setDockPaneTitle, setEntityDataSource, setEntityForm, setFieldConditions, setFieldHidden, setFieldInput, setFormColumns, setHandlerBody, setHandlerSteps, setJob, setKitVersion, setLayoutOpts, setScreenDock, setScreenHandlersSource, setScreenLayout, setScreenRenderGrid, setScreenRenderMode, setShell, setTenancy, setTheme, setThemePreset, setTrigger, skipUserOwned, splitOpts, ssrEligible, ssrScreenShape, stackOpts, starterDatasets, starterProject, stateInitExpr, stateTsType, statusPills, stepsToCode, studioDeployInfo, studioThemes, suggestFormSections, summarizeVerify, syncDockPanes, tenantField, themeStyleString, toSvelteKit3, triggersOf, uiComponentSpec, updateBlock, updateEntity, updateEntityField, updateFormSection, updateHandlerStep, updateScreen, updateStateVar, validateProject, verifyScaffold, withSsrDefaults };
|