@svgrid/enterprise 2.2.1 → 2.3.1
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/README.md +95 -81
- package/dist/cdn/svgrid-enterprise.svelte-external.js +26642 -11252
- package/dist/designer/assets/GridMenus-BtWVk9Ab.js +7 -0
- package/dist/designer/assets/SvGridChartPanel-DWlBO2SD.js +10 -0
- package/dist/designer/assets/SvGridChartView-8c8Mb4j8.js +1 -0
- package/dist/designer/assets/SvGridChartView-DX9HfkBR.css +1 -0
- package/dist/designer/assets/index-DsDgp9Xq.js +78758 -0
- package/dist/designer/assets/index-tTY_Dx4P.css +1 -0
- package/dist/designer/assets/jszip.min-fkJdmAmj.js +2 -0
- package/dist/designer/assets/pdfmake-DeCsnyl9.js +242 -0
- package/dist/designer/assets/smart.export-BZlSCE8T.js +35 -0
- package/dist/designer/assets/vfs_fonts-eX2NpmfX.js +1 -0
- package/dist/designer/index.html +13 -0
- package/dist/node/studio.js +15304 -2708
- package/package.json +35 -11
- package/src/SvAlertRuleEditor.svelte +294 -0
- package/src/SvAlertsManager.svelte +210 -0
- package/src/SvAlertsPanel.svelte +129 -0
- package/src/SvBoard.svelte +3 -1
- package/src/SvExpressionEditor.svelte +341 -0
- package/src/SvGridAlerts.dom.test.ts +113 -0
- package/src/SvGridAlerts.svelte +265 -0
- package/src/SvGridBoard.svelte +2358 -0
- package/src/SvGridEditPanel.svelte +849 -794
- package/src/SvGridScheduler.svelte +5358 -4410
- package/src/SvPivotDesigner.svelte +3 -3
- package/src/SvRecordDetail.svelte +6 -2
- package/src/SvSchedule.svelte +3 -1
- package/src/{ai-export-pdf.test.ts → ai-export-pdf.dom.test.ts} +77 -74
- package/src/{ai-export-xlsx.test.ts → ai-export-xlsx.dom.test.ts} +90 -87
- package/src/{ai-export.test.ts → ai-export.dom.test.ts} +114 -110
- package/src/alerts/alert-engine-attach.ts +165 -0
- package/src/alerts/alert-engine.test.ts +135 -0
- package/src/alerts/alert-engine.ts +260 -0
- package/src/alerts/alert-formats.test.ts +87 -0
- package/src/alerts/alert-formats.ts +77 -0
- package/src/alerts/alert-observer.test.ts +189 -0
- package/src/alerts/alert-observer.ts +208 -0
- package/src/alerts/alert-scheduler.ts +96 -0
- package/src/alerts/alert-storage.test.ts +54 -0
- package/src/alerts/alert-storage.ts +116 -0
- package/src/alerts/alert-store.svelte.ts +80 -0
- package/src/alerts/alert-types.ts +94 -0
- package/src/alerts.ts +28 -0
- package/src/board.dom.test.ts +941 -0
- package/src/board.ts +36 -0
- package/src/export-ooxml.ts +4 -0
- package/src/export-xls.ts +3 -0
- package/src/export.ts +6 -0
- package/src/expressions/evaluate.test.ts +111 -0
- package/src/expressions/evaluate.ts +237 -0
- package/src/expressions/expression-columns.ts +133 -0
- package/src/expressions/expression-types.ts +84 -0
- package/src/expressions/parse.test.ts +106 -0
- package/src/expressions/parse.ts +610 -0
- package/src/import.test.ts +1 -1
- package/src/import.ts +3 -1
- package/src/index.ts +192 -46
- package/src/install.ts +12 -2
- package/src/pivot-enable.ts +44 -0
- package/src/pivot.test.ts +0 -1
- package/src/scheduler-assignments.test.ts +97 -0
- package/src/scheduler-assignments.ts +134 -0
- package/src/scheduler-axis.test.ts +108 -0
- package/src/scheduler-axis.ts +238 -0
- package/src/scheduler-booking.test.ts +57 -0
- package/src/scheduler-booking.ts +63 -0
- package/src/scheduler-config.ts +179 -0
- package/src/scheduler-dependencies.test.ts +155 -0
- package/src/scheduler-dependencies.ts +223 -0
- package/src/scheduler-freebusy.test.ts +41 -0
- package/src/scheduler-freebusy.ts +36 -0
- package/src/scheduler-heatmap.test.ts +39 -0
- package/src/scheduler-heatmap.ts +55 -0
- package/src/scheduler-resource-tree.test.ts +84 -0
- package/src/scheduler-resource-tree.ts +107 -0
- package/src/scheduler-slots.test.ts +53 -0
- package/src/scheduler-slots.ts +94 -0
- package/src/scheduler-summary.test.ts +47 -0
- package/src/scheduler-summary.ts +81 -0
- package/src/schema-designer.ts +1 -1
- package/src/sources/index.ts +1 -1
- package/src/sources/introspect-supabase.test.ts +13 -1
- package/src/sources/introspect-supabase.ts +20 -0
- package/src/studio/copilot-core.test.ts +45 -0
- package/src/studio/copilot-core.ts +65 -0
- package/src/studio/deploy-cli.test.ts +56 -0
- package/src/studio/deploy-cli.ts +100 -0
- package/src/studio/emit-project.test.ts +800 -24
- package/src/studio/emit-project.ts +1307 -135
- package/src/studio/emit-schema.test.ts +17 -0
- package/src/studio/emit-schema.ts +182 -41
- package/src/studio/index.ts +59 -1
- package/src/studio/init-flow.test.ts +239 -0
- package/src/studio/init-flow.ts +358 -0
- package/src/studio/introspect-openapi.test.ts +84 -0
- package/src/studio/introspect-openapi.ts +252 -0
- package/src/studio/project.test.ts +57 -1
- package/src/studio/project.ts +302 -8
- package/src/studio/samples/crm.ts +282 -258
- package/src/studio/samples/datasets.test.ts +84 -0
- package/src/studio/samples/datasets.ts +340 -0
- package/src/studio/samples/fleet.ts +213 -186
- package/src/studio/samples/insurance.ts +223 -195
- package/src/studio/samples/inventory.ts +213 -182
- package/src/studio/samples/live-data.test.ts +99 -98
- package/src/studio/samples/live-data.ts +8 -10
- package/src/studio/samples/projects.ts +212 -190
- package/src/studio/samples/samples.test.ts +268 -216
- package/src/studio/samples/shared.ts +22 -145
- package/src/studio/samples/starter.ts +251 -0
- package/src/studio/samples/support.ts +209 -184
- package/src/studio/screen-suites.test.ts +226 -0
- package/src/studio/screen-suites.ts +445 -0
- package/src/studio/ui-components-surface.test.ts +185 -0
- package/src/studio/ui-components.generated.ts +7154 -0
- package/src/studio/ui-components.ts +273 -3
- package/src/sveltekit/index.ts +1 -0
- package/src/sveltekit/sql-source.test.ts +13 -0
- package/src/sveltekit/sql-source.ts +12 -7
- package/src/sveltekit/transport-scope.test.ts +125 -0
- package/src/sveltekit/transport.ts +76 -2
- package/src/upgrade-prompt.ts +2 -2
- package/src/watermark.ts +2 -2
- package/src/ai.test.ts +0 -522
- package/src/ai.ts +0 -1388
|
@@ -1,98 +1,99 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { compile } from 'svelte/compiler'
|
|
3
|
-
import { liveDataSamples, getLiveDataSample } from './live-data.js'
|
|
4
|
-
import { validateProject, parseProject, serializeProject, type EntityDataSource } from '../project.js'
|
|
5
|
-
import { emitStudioProject } from '../emit-project.js'
|
|
6
|
-
|
|
7
|
-
describe('live-data starters', () => {
|
|
8
|
-
it('ships a gallery with unique ids and card metadata', () => {
|
|
9
|
-
expect(liveDataSamples.length).toBeGreaterThanOrEqual(3)
|
|
10
|
-
const ids = liveDataSamples.map((s) => s.id)
|
|
11
|
-
expect(new Set(ids).size).toBe(ids.length)
|
|
12
|
-
for (const s of liveDataSamples) {
|
|
13
|
-
expect(s.name).toBeTruthy()
|
|
14
|
-
expect(s.description).toBeTruthy()
|
|
15
|
-
expect(s.emoji).toBeTruthy()
|
|
16
|
-
expect(s.accent).toMatch(/^#[0-9a-f]{6}$/i)
|
|
17
|
-
}
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('getLiveDataSample resolves by id', () => {
|
|
21
|
-
expect(getLiveDataSample('live-northwind-pglite')?.name).toBe('Northwind · Local Postgres')
|
|
22
|
-
expect(getLiveDataSample('nope')).toBeUndefined()
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
for (const sample of liveDataSamples) {
|
|
26
|
-
describe(sample.id, () => {
|
|
27
|
-
const project = sample.build()
|
|
28
|
-
|
|
29
|
-
it('is a valid project (no errors)', () => {
|
|
30
|
-
const errors = validateProject(project).filter((i) => i.level === 'error')
|
|
31
|
-
expect(errors, JSON.stringify(errors)).toHaveLength(0)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('binds every entity to a REAL source (never memory)', () => {
|
|
35
|
-
expect(project.entities.length).toBeGreaterThanOrEqual(1)
|
|
36
|
-
for (const e of project.entities) {
|
|
37
|
-
const src = project.dataSources?.[e.name] as EntityDataSource | undefined
|
|
38
|
-
expect(src, `${e.name}: no source`).toBeTruthy()
|
|
39
|
-
expect(src!.kind).not.toBe('memory')
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('round-trips through parse/serialize', () => {
|
|
44
|
-
const again = parseProject(serializeProject(project))
|
|
45
|
-
expect(again.entities.map((e) => e.name)).toEqual(project.entities.map((e) => e.name))
|
|
46
|
-
expect(again.dataSources).toEqual(project.dataSources)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('generates compiling Svelte pages', () => {
|
|
50
|
-
const files = emitStudioProject(project)
|
|
51
|
-
for (const f of files.filter((f) => f.path.endsWith('.svelte'))) {
|
|
52
|
-
expect(() => compile(f.contents, { filename: f.path, generate: 'client' }), f.path).not.toThrow()
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
it('every starter is a real app: a dashboard, a detail page, and drill-through', () => {
|
|
59
|
-
for (const s of liveDataSamples) {
|
|
60
|
-
const blocks = s.build().screens.flatMap((sc) => sc.blocks)
|
|
61
|
-
expect(blocks.some((b) => b.config.kind === 'kpi'), `${s.id}: no dashboard KPIs`).toBe(true)
|
|
62
|
-
const all = emitStudioProject(s.build()).map((f) => f.contents).join('\n')
|
|
63
|
-
expect(all.includes('SvRecordDetail'), `${s.id}: no detail page`).toBe(true)
|
|
64
|
-
expect(all.includes('import { goto }'), `${s.id}: no drill-through`).toBe(true)
|
|
65
|
-
expect(all.includes("selectedId={
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('the Northwind (Supabase) detail timelines match the denormalized view by name/company', () => {
|
|
70
|
-
const all = emitStudioProject(getLiveDataSample('live-northwind-supabase')!.build()).map((f) => f.contents).join('\n')
|
|
71
|
-
expect(all).toContain('parentField: "name"') // product -> order_lines by product name
|
|
72
|
-
expect(all).toContain('parentField: "company"') // customer -> order_lines by company
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('PGlite starter generates embedded Postgres bootstrap + seeded rows', () => {
|
|
76
|
-
const data = emitStudioProject(getLiveDataSample('live-northwind-pglite')!.build()).find((f) => f.path.endsWith('src/lib/data.ts'))!.contents
|
|
77
|
-
expect(data).toContain("import { PGlite } from '@electric-sql/pglite'")
|
|
78
|
-
expect(data).toContain('createSqlDataSource')
|
|
79
|
-
expect(data).toContain('Chai') // a curated seed row is baked in
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('Supabase starter generates a client bound to the hosted sample', () => {
|
|
83
|
-
const files = emitStudioProject(getLiveDataSample('live-northwind-supabase')!.build())
|
|
84
|
-
const conn = files.find((f) => f.path.endsWith('src/lib/connections.ts'))!.contents
|
|
85
|
-
expect(conn).toContain('rbnnlzgtfzsylllniozo.supabase.co')
|
|
86
|
-
expect(conn).toContain('createClient')
|
|
87
|
-
const data = files.find((f) => f.path.endsWith('src/lib/data.ts'))!.contents
|
|
88
|
-
expect(data).toContain('createSupabaseDataSource')
|
|
89
|
-
expect(data).toContain("table: 'order_lines'")
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('REST starter generates createRestDataSource against DummyJSON', () => {
|
|
93
|
-
const data = emitStudioProject(getLiveDataSample('live-dummyjson-rest')!.build()).find((f) => f.path.endsWith('src/lib/data.ts'))!.contents
|
|
94
|
-
expect(data).toContain('createRestDataSource')
|
|
95
|
-
expect(data).toContain('dummyjson.com/products')
|
|
96
|
-
expect(data).toContain('
|
|
97
|
-
|
|
98
|
-
})
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { compile } from 'svelte/compiler'
|
|
3
|
+
import { liveDataSamples, getLiveDataSample } from './live-data.js'
|
|
4
|
+
import { validateProject, parseProject, serializeProject, type EntityDataSource } from '../project.js'
|
|
5
|
+
import { emitStudioProject } from '../emit-project.js'
|
|
6
|
+
|
|
7
|
+
describe('live-data starters', () => {
|
|
8
|
+
it('ships a gallery with unique ids and card metadata', () => {
|
|
9
|
+
expect(liveDataSamples.length).toBeGreaterThanOrEqual(3)
|
|
10
|
+
const ids = liveDataSamples.map((s) => s.id)
|
|
11
|
+
expect(new Set(ids).size).toBe(ids.length)
|
|
12
|
+
for (const s of liveDataSamples) {
|
|
13
|
+
expect(s.name).toBeTruthy()
|
|
14
|
+
expect(s.description).toBeTruthy()
|
|
15
|
+
expect(s.emoji).toBeTruthy()
|
|
16
|
+
expect(s.accent).toMatch(/^#[0-9a-f]{6}$/i)
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('getLiveDataSample resolves by id', () => {
|
|
21
|
+
expect(getLiveDataSample('live-northwind-pglite')?.name).toBe('Northwind · Local Postgres')
|
|
22
|
+
expect(getLiveDataSample('nope')).toBeUndefined()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
for (const sample of liveDataSamples) {
|
|
26
|
+
describe(sample.id, () => {
|
|
27
|
+
const project = sample.build()
|
|
28
|
+
|
|
29
|
+
it('is a valid project (no errors)', () => {
|
|
30
|
+
const errors = validateProject(project).filter((i) => i.level === 'error')
|
|
31
|
+
expect(errors, JSON.stringify(errors)).toHaveLength(0)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('binds every entity to a REAL source (never memory)', () => {
|
|
35
|
+
expect(project.entities.length).toBeGreaterThanOrEqual(1)
|
|
36
|
+
for (const e of project.entities) {
|
|
37
|
+
const src = project.dataSources?.[e.name] as EntityDataSource | undefined
|
|
38
|
+
expect(src, `${e.name}: no source`).toBeTruthy()
|
|
39
|
+
expect(src!.kind).not.toBe('memory')
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('round-trips through parse/serialize', () => {
|
|
44
|
+
const again = parseProject(serializeProject(project))
|
|
45
|
+
expect(again.entities.map((e) => e.name)).toEqual(project.entities.map((e) => e.name))
|
|
46
|
+
expect(again.dataSources).toEqual(project.dataSources)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('generates compiling Svelte pages', () => {
|
|
50
|
+
const files = emitStudioProject(project)
|
|
51
|
+
for (const f of files.filter((f) => f.path.endsWith('.svelte'))) {
|
|
52
|
+
expect(() => compile(f.contents, { filename: f.path, generate: 'client' }), f.path).not.toThrow()
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
it('every starter is a real app: a dashboard, a detail page, and drill-through', () => {
|
|
59
|
+
for (const s of liveDataSamples) {
|
|
60
|
+
const blocks = s.build().screens.flatMap((sc) => sc.blocks)
|
|
61
|
+
expect(blocks.some((b) => b.config.kind === 'kpi'), `${s.id}: no dashboard KPIs`).toBe(true)
|
|
62
|
+
const all = emitStudioProject(s.build()).map((f) => f.contents).join('\n')
|
|
63
|
+
expect(all.includes('SvRecordDetail'), `${s.id}: no detail page`).toBe(true)
|
|
64
|
+
expect(all.includes('import { goto }'), `${s.id}: no drill-through`).toBe(true)
|
|
65
|
+
expect(all.includes("selectedId={page.url.searchParams.get('id')"), `${s.id}: detail not URL-addressable`).toBe(true)
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('the Northwind (Supabase) detail timelines match the denormalized view by name/company', () => {
|
|
70
|
+
const all = emitStudioProject(getLiveDataSample('live-northwind-supabase')!.build()).map((f) => f.contents).join('\n')
|
|
71
|
+
expect(all).toContain('parentField: "name"') // product -> order_lines by product name
|
|
72
|
+
expect(all).toContain('parentField: "company"') // customer -> order_lines by company
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('PGlite starter generates embedded Postgres bootstrap + seeded rows', () => {
|
|
76
|
+
const data = emitStudioProject(getLiveDataSample('live-northwind-pglite')!.build()).find((f) => f.path.endsWith('src/lib/data.ts'))!.contents
|
|
77
|
+
expect(data).toContain("import { PGlite } from '@electric-sql/pglite'")
|
|
78
|
+
expect(data).toContain('createSqlDataSource')
|
|
79
|
+
expect(data).toContain('Chai') // a curated seed row is baked in
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('Supabase starter generates a client bound to the hosted sample', () => {
|
|
83
|
+
const files = emitStudioProject(getLiveDataSample('live-northwind-supabase')!.build())
|
|
84
|
+
const conn = files.find((f) => f.path.endsWith('src/lib/connections.ts'))!.contents
|
|
85
|
+
expect(conn).toContain('rbnnlzgtfzsylllniozo.supabase.co')
|
|
86
|
+
expect(conn).toContain('createClient')
|
|
87
|
+
const data = files.find((f) => f.path.endsWith('src/lib/data.ts'))!.contents
|
|
88
|
+
expect(data).toContain('createSupabaseDataSource')
|
|
89
|
+
expect(data).toContain("table: 'order_lines'")
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('REST starter generates createRestDataSource against DummyJSON', () => {
|
|
93
|
+
const data = emitStudioProject(getLiveDataSample('live-dummyjson-rest')!.build()).find((f) => f.path.endsWith('src/lib/data.ts'))!.contents
|
|
94
|
+
expect(data).toContain('createRestDataSource')
|
|
95
|
+
expect(data).toContain('dummyjson.com/products')
|
|
96
|
+
expect(data).toContain('dummyJsonAdapter') // real server paging via the adapter (skip/limit/sortBy)
|
|
97
|
+
expect(data).not.toContain('body?.products') // adapter unwraps { products, total }, no manual parse
|
|
98
|
+
})
|
|
99
|
+
})
|
|
@@ -260,10 +260,9 @@ const dummyjsonRest: SampleApp = {
|
|
|
260
260
|
preset: 'shadcn',
|
|
261
261
|
dataSource: 'rest',
|
|
262
262
|
entities: [restProduct],
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
// see docs/enterprise/studio/rest-api.md.)
|
|
263
|
+
// The DummyJSON REST adapter gives the grid REAL server-side paging + sort
|
|
264
|
+
// (skip/limit/sortBy), and the dashboard aggregates fetch the catalogue in one
|
|
265
|
+
// large page. No pageSize hack, no manual row/total mapping.
|
|
267
266
|
screens: [
|
|
268
267
|
// Catalog dashboard: price / rating / stock KPIs + category & brand breakdowns
|
|
269
268
|
// over the live feed, with the grid drilling into a product page.
|
|
@@ -275,7 +274,7 @@ const dummyjsonRest: SampleApp = {
|
|
|
275
274
|
{ chart: 'category', reduce: 'count', type: 'bar', span: 2 },
|
|
276
275
|
{ chart: 'brand', measure: 'price', reduce: 'avg', type: 'bar', span: 1 },
|
|
277
276
|
{ filter: ['category', 'brand'], span: 3 },
|
|
278
|
-
{ grid: true,
|
|
277
|
+
{ grid: true, rowLink: { screen: 'product-detail', sourceField: 'id', targetField: 'id' }, span: 3 },
|
|
279
278
|
]),
|
|
280
279
|
detailScreen(restProduct, { id: 'product-detail', title: 'Product', order: 1 }, {
|
|
281
280
|
titleField: 'title', subtitleField: 'brand', metricFields: ['price', 'rating', 'stock'],
|
|
@@ -283,16 +282,15 @@ const dummyjsonRest: SampleApp = {
|
|
|
283
282
|
}),
|
|
284
283
|
],
|
|
285
284
|
sources: {
|
|
286
|
-
//
|
|
287
|
-
//
|
|
285
|
+
// The dummyjson adapter maps skip/limit/sortBy + unwraps the { products, total }
|
|
286
|
+
// envelope, so the grid pages + sorts against the live feed for real.
|
|
288
287
|
products: {
|
|
289
288
|
kind: 'rest',
|
|
290
289
|
baseUrl: 'https://dummyjson.com',
|
|
291
290
|
path: 'products',
|
|
292
291
|
method: 'GET',
|
|
293
|
-
params: [
|
|
294
|
-
|
|
295
|
-
totalPath: 'total',
|
|
292
|
+
params: [],
|
|
293
|
+
adapter: { kind: 'dummyjson', rowsKey: 'products' },
|
|
296
294
|
},
|
|
297
295
|
},
|
|
298
296
|
})
|