@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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert-rule persistence + sharing. Mirrors the grid's `named-views` storage
|
|
3
|
+
* shape: a pluggable `AlertRulesStorage` (in-memory or localStorage), wrapped by
|
|
4
|
+
* a small CRUD manager. `export()` / `import()` make a rule set shareable as
|
|
5
|
+
* JSON, so a team lead can hand round a set of alerts.
|
|
6
|
+
*/
|
|
7
|
+
import type { AlertRule } from './alert-types'
|
|
8
|
+
|
|
9
|
+
export type AlertRulesStorage = {
|
|
10
|
+
read(): AlertRule[]
|
|
11
|
+
write(rules: AlertRule[]): void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** In-memory storage (default). State lives only as long as the manager. */
|
|
15
|
+
export function memoryAlertRules(initial: AlertRule[] = []): AlertRulesStorage {
|
|
16
|
+
let rules = [...initial]
|
|
17
|
+
return {
|
|
18
|
+
read: () => rules,
|
|
19
|
+
write: (next) => {
|
|
20
|
+
rules = next
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** localStorage-backed storage. Safe to construct under SSR (no-ops there). */
|
|
26
|
+
export function localStorageAlertRules(key: string): AlertRulesStorage {
|
|
27
|
+
const available = typeof localStorage !== 'undefined'
|
|
28
|
+
return {
|
|
29
|
+
read() {
|
|
30
|
+
if (!available) return []
|
|
31
|
+
try {
|
|
32
|
+
const raw = localStorage.getItem(key)
|
|
33
|
+
const parsed = raw ? JSON.parse(raw) : []
|
|
34
|
+
return Array.isArray(parsed) ? (parsed as AlertRule[]) : []
|
|
35
|
+
} catch {
|
|
36
|
+
return []
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
write(rules) {
|
|
40
|
+
if (!available) return
|
|
41
|
+
try {
|
|
42
|
+
localStorage.setItem(key, JSON.stringify(rules))
|
|
43
|
+
} catch {
|
|
44
|
+
// quota / private-mode - persistence is best-effort
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type AlertRulesManager = {
|
|
51
|
+
list(): AlertRule[]
|
|
52
|
+
get(id: string): AlertRule | undefined
|
|
53
|
+
/** Insert or replace a rule (matched by id). Returns the saved rule. */
|
|
54
|
+
save(rule: AlertRule): AlertRule
|
|
55
|
+
remove(id: string): boolean
|
|
56
|
+
rename(id: string, name: string): boolean
|
|
57
|
+
/** Flip a rule's `enabled` flag. Returns the new value (or undefined). */
|
|
58
|
+
toggle(id: string): boolean | undefined
|
|
59
|
+
/** Serialise the whole rule set to shareable JSON. */
|
|
60
|
+
export(): string
|
|
61
|
+
/** Replace the rule set from JSON produced by `export()`. */
|
|
62
|
+
import(json: string): AlertRule[]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** CRUD manager over a storage adapter (defaults to in-memory). */
|
|
66
|
+
export function createAlertRules(
|
|
67
|
+
storage: AlertRulesStorage = memoryAlertRules(),
|
|
68
|
+
): AlertRulesManager {
|
|
69
|
+
const load = () => storage.read()
|
|
70
|
+
const commit = (rules: AlertRule[]) => storage.write(rules)
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
list: () => load(),
|
|
74
|
+
get: (id) => load().find((r) => r.id === id),
|
|
75
|
+
save(rule) {
|
|
76
|
+
const rules = load()
|
|
77
|
+
const i = rules.findIndex((r) => r.id === rule.id)
|
|
78
|
+
if (i >= 0) rules[i] = rule
|
|
79
|
+
else rules.push(rule)
|
|
80
|
+
commit(rules)
|
|
81
|
+
return rule
|
|
82
|
+
},
|
|
83
|
+
remove(id) {
|
|
84
|
+
const rules = load()
|
|
85
|
+
const next = rules.filter((r) => r.id !== id)
|
|
86
|
+
if (next.length === rules.length) return false
|
|
87
|
+
commit(next)
|
|
88
|
+
return true
|
|
89
|
+
},
|
|
90
|
+
rename(id, name) {
|
|
91
|
+
const rules = load()
|
|
92
|
+
const rule = rules.find((r) => r.id === id)
|
|
93
|
+
if (!rule) return false
|
|
94
|
+
rule.name = name
|
|
95
|
+
commit(rules)
|
|
96
|
+
return true
|
|
97
|
+
},
|
|
98
|
+
toggle(id) {
|
|
99
|
+
const rules = load()
|
|
100
|
+
const rule = rules.find((r) => r.id === id)
|
|
101
|
+
if (!rule) return undefined
|
|
102
|
+
rule.enabled = !rule.enabled
|
|
103
|
+
commit(rules)
|
|
104
|
+
return rule.enabled
|
|
105
|
+
},
|
|
106
|
+
export() {
|
|
107
|
+
return JSON.stringify(load(), null, 2)
|
|
108
|
+
},
|
|
109
|
+
import(json) {
|
|
110
|
+
const parsed = JSON.parse(json)
|
|
111
|
+
const rules = Array.isArray(parsed) ? (parsed as AlertRule[]) : []
|
|
112
|
+
commit(rules)
|
|
113
|
+
return rules
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* alert-store - the reactive log behind the alerts badge + panel. A singleton
|
|
3
|
+
* queue of fired `AlertEvent`s (newest first) with an unacknowledged count,
|
|
4
|
+
* mirroring the grid's `toast-store` shape. The observer pushes events here; the
|
|
5
|
+
* badge reads `activeCount`; the panel lists, acknowledges, and clears them.
|
|
6
|
+
*
|
|
7
|
+
* Perf: a burst of events from one evaluation is written in ONE array update via
|
|
8
|
+
* `pushMany` (not one reassignment per event), and `activeCount` is a maintained
|
|
9
|
+
* counter - not an O(n) reduce recomputed on every render.
|
|
10
|
+
*/
|
|
11
|
+
import type { AlertEvent } from './alert-types'
|
|
12
|
+
|
|
13
|
+
const MAX = 500
|
|
14
|
+
|
|
15
|
+
function createAlertStore() {
|
|
16
|
+
let events = $state<AlertEvent[]>([])
|
|
17
|
+
let active = $state(0)
|
|
18
|
+
let seq = 0
|
|
19
|
+
|
|
20
|
+
/** Recompute the unacknowledged counter after a trim/clear/ack. */
|
|
21
|
+
function recount(list: ReadonlyArray<AlertEvent>): number {
|
|
22
|
+
let n = 0
|
|
23
|
+
for (const e of list) if (!e.acknowledged) n++
|
|
24
|
+
return n
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
get events() {
|
|
29
|
+
return events
|
|
30
|
+
},
|
|
31
|
+
/** Number of unacknowledged events (what the badge shows). */
|
|
32
|
+
get activeCount() {
|
|
33
|
+
return active
|
|
34
|
+
},
|
|
35
|
+
/** Append one event (kept for API compatibility; prefer `pushMany`). */
|
|
36
|
+
push(event: AlertEvent) {
|
|
37
|
+
this.pushMany([event])
|
|
38
|
+
},
|
|
39
|
+
/** Append a batch of events in a single reactive update (newest first). */
|
|
40
|
+
pushMany(batch: ReadonlyArray<AlertEvent>) {
|
|
41
|
+
if (batch.length === 0) return
|
|
42
|
+
const tagged: AlertEvent[] = []
|
|
43
|
+
let added = 0
|
|
44
|
+
// Newest first: reverse so the last event in the batch ends up on top.
|
|
45
|
+
for (let i = batch.length - 1; i >= 0; i--) {
|
|
46
|
+
tagged.push({ ...batch[i]!, _id: ++seq } as AlertEvent & { _id: number })
|
|
47
|
+
if (!batch[i]!.acknowledged) added++
|
|
48
|
+
}
|
|
49
|
+
let next = [...tagged, ...events]
|
|
50
|
+
if (next.length > MAX) {
|
|
51
|
+
next = next.slice(0, MAX)
|
|
52
|
+
// A trim can drop unacknowledged events - recount to stay exact.
|
|
53
|
+
active = recount(next)
|
|
54
|
+
} else {
|
|
55
|
+
active += added
|
|
56
|
+
}
|
|
57
|
+
events = next
|
|
58
|
+
},
|
|
59
|
+
acknowledge(index: number) {
|
|
60
|
+
const next = events.slice()
|
|
61
|
+
const cur = next[index]
|
|
62
|
+
if (cur && !cur.acknowledged) {
|
|
63
|
+
next[index] = { ...cur, acknowledged: true }
|
|
64
|
+
active = Math.max(0, active - 1)
|
|
65
|
+
events = next
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
acknowledgeAll() {
|
|
69
|
+
events = events.map((e) => (e.acknowledged ? e : { ...e, acknowledged: true }))
|
|
70
|
+
active = 0
|
|
71
|
+
},
|
|
72
|
+
clear() {
|
|
73
|
+
events = []
|
|
74
|
+
active = 0
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The shared alerts log. Read `.events` / `.activeCount` inside a component. */
|
|
80
|
+
export const alertStore = createAlertStore()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert Rules engine - the model layer.
|
|
3
|
+
*
|
|
4
|
+
* An alert rule is a declarative "when this predicate holds (or this value
|
|
5
|
+
* moves), do these things": raise a toast, paint a badge/highlight, flash the
|
|
6
|
+
* cell, veto the edit, or log it. Rules are authored by end users at runtime,
|
|
7
|
+
* persisted, and shareable - the AdapTable-style alerting surface, built on the
|
|
8
|
+
* grid's own filter/conditional-format engines.
|
|
9
|
+
*
|
|
10
|
+
* Predicates and change tests come from the expression layer (`../expressions`).
|
|
11
|
+
*/
|
|
12
|
+
import type { ChangeExpr, PredicateExpr } from '../expressions/expression-types'
|
|
13
|
+
import type { Schedule } from '../scheduling'
|
|
14
|
+
|
|
15
|
+
/** Maps onto the toast variants + conditional-format intent. */
|
|
16
|
+
export type AlertSeverity = 'info' | 'success' | 'warning' | 'error'
|
|
17
|
+
|
|
18
|
+
/** What the rule watches over. */
|
|
19
|
+
export type AlertScope = 'row' | 'cell' | 'aggregate'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* When a rule fires:
|
|
23
|
+
* - `dataChange` - a row newly satisfies `predicate` after an edit/update.
|
|
24
|
+
* - `relativeChange` - a value moved (delta / percent / crossed a threshold).
|
|
25
|
+
* - `validation` - evaluated on edit; may veto the change (`preventEdit`).
|
|
26
|
+
* - `scheduled` - re-checked on a cron/one-off schedule (reuses the enterprise
|
|
27
|
+
* scheduler); surfaces rows currently matching `predicate`.
|
|
28
|
+
*/
|
|
29
|
+
export type AlertTrigger =
|
|
30
|
+
| { type: 'dataChange' }
|
|
31
|
+
| { type: 'relativeChange'; expr: ChangeExpr }
|
|
32
|
+
| { type: 'validation' }
|
|
33
|
+
| { type: 'scheduled'; schedule: Schedule }
|
|
34
|
+
|
|
35
|
+
/** A thing the rule does when it fires. */
|
|
36
|
+
export type AlertActionKind =
|
|
37
|
+
| 'toast'
|
|
38
|
+
| 'badge'
|
|
39
|
+
| 'cellFlash'
|
|
40
|
+
| 'highlight'
|
|
41
|
+
| 'preventEdit'
|
|
42
|
+
| 'log'
|
|
43
|
+
|
|
44
|
+
/** Visual styling for `badge` / `highlight` actions. */
|
|
45
|
+
export type AlertActionStyle = {
|
|
46
|
+
background?: string
|
|
47
|
+
color?: string
|
|
48
|
+
/** A short glyph/emoji shown as a badge. */
|
|
49
|
+
icon?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type AlertAction = {
|
|
53
|
+
kind: AlertActionKind
|
|
54
|
+
/**
|
|
55
|
+
* Message template. Tokens: `{rule}`, `{severity}`, `{column}`, `{value}`,
|
|
56
|
+
* and `{field}` / `{row.field}` for any row field.
|
|
57
|
+
*/
|
|
58
|
+
message?: string
|
|
59
|
+
style?: AlertActionStyle
|
|
60
|
+
/** Columns the badge/highlight/flash targets (defaults to the rule's). */
|
|
61
|
+
columns?: string[]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type AlertRule = {
|
|
65
|
+
id: string
|
|
66
|
+
name: string
|
|
67
|
+
enabled: boolean
|
|
68
|
+
severity: AlertSeverity
|
|
69
|
+
scope: AlertScope
|
|
70
|
+
predicate: PredicateExpr
|
|
71
|
+
trigger: AlertTrigger
|
|
72
|
+
actions: AlertAction[]
|
|
73
|
+
/** Columns in scope for cell-level styling (badge/highlight/flash). */
|
|
74
|
+
columns?: string[]
|
|
75
|
+
createdAt: number
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** A single firing of a rule. */
|
|
79
|
+
export type AlertEvent = {
|
|
80
|
+
ruleId: string
|
|
81
|
+
ruleName: string
|
|
82
|
+
severity: AlertSeverity
|
|
83
|
+
scope: AlertScope
|
|
84
|
+
triggerType: AlertTrigger['type']
|
|
85
|
+
/** The row that matched (absent for aggregate-scope events). */
|
|
86
|
+
rowId?: string
|
|
87
|
+
/** The column that triggered/targets the event, when applicable. */
|
|
88
|
+
columnId?: string
|
|
89
|
+
value?: unknown
|
|
90
|
+
message: string
|
|
91
|
+
actions: AlertAction[]
|
|
92
|
+
firedAt: number
|
|
93
|
+
acknowledged?: boolean
|
|
94
|
+
}
|
package/src/alerts.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alerts (Pro) - the public entry for the Alert Rules engine + expression layer.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the scheduler/board *views*, alerts are an overlay the consumer mounts
|
|
5
|
+
* next to the grid (`<SvGridAlerts>`), so there is no renderer to register with
|
|
6
|
+
* the grid. `enableAlerts()` exists only to keep the Pro soft-gate consistent
|
|
7
|
+
* with the rest of Enterprise: it works without a license key but nudges once in
|
|
8
|
+
* the console when unlicensed.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { setLicenseKey, enableAlerts } from '@svgrid/enterprise'
|
|
12
|
+
* setLicenseKey('YOUR-KEY')
|
|
13
|
+
* enableAlerts()
|
|
14
|
+
* // then mount <SvGridAlerts data={rows} columns={cols} bind:formats /> and
|
|
15
|
+
* // spread `formats` into <SvGrid conditionalFormats={[...base, ...formats]} />
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { isLicenseKeySet } from './license'
|
|
19
|
+
import { emitUnlicensedNudge } from './watermark'
|
|
20
|
+
|
|
21
|
+
let enabled = false
|
|
22
|
+
|
|
23
|
+
/** Turn on the alerts feature (soft-gated). Idempotent. */
|
|
24
|
+
export function enableAlerts(): void {
|
|
25
|
+
if (enabled) return
|
|
26
|
+
enabled = true
|
|
27
|
+
if (!isLicenseKeySet()) emitUnlicensedNudge()
|
|
28
|
+
}
|