@book.dev/sdk 2.0.0 → 2.2.0

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/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export type { PageSnapshot, PageMeta, StoredPage, PageInput, ServerInfo, ServerControls, PageVisibility, Visibility, MemberRole, EffectiveRole, MemberStatus, MemberSource, AclLevel, Member, PageAcl, } from './types';
2
2
  export { emptyPageSnapshot, PAGE_VISIBILITIES } from './types';
3
3
  export { API, type ApiError, type CompactResult } from './routes';
4
- export type { DataClient, PageSubscription, FetchLike, LiveSourceLike, HttpDataClientOptions, IdentityCredential } from './client';
4
+ export type { DataClient, PageSubscription, FetchLike, LiveSourceLike, HttpDataClientOptions, IdentityCredential, CreateAgentTokenInput, CreatedAgentToken, AgentTokenList } from './client';
5
5
  export { HttpDataClient } from './client';
6
6
  export { ForwardingApiError, ForwardingClient, TunnelClient, MemoryKeyStore, SITE_VISIBILITIES, mintSiteKeypair, signWithSiteKey, verifyWithSiteKey, FORWARDED_HEADER, signRosterAssertion, verifyRosterAssertion, isAcceptedRosterVersion, ROSTER_ASSERTION_VERSION, ROSTER_ASSERTION_V2, ACCEPTED_ROSTER_VERSIONS, ROSTER_ASSERTION_SKEW_MS, type KeyStore, type SiteIdentity, type SiteVisibility, type ForwardingClientOptions, type TunnelStatus, type RosterAssertionVersion, type RosterAssertionPayload, type RosterAssertionV1Payload, type RosterAssertionV2Payload, type SignRosterAssertionInput, type VerifyRosterAssertionInput, type VerifiedRosterAssertion, } from './forwarding';
7
7
  export { DATA_PALETTE, DATA_COLOR_TOKENS, DATA_COLOR_SCHEMES, DEFAULT_DATA_COLOR_SCHEME, SERIES_ORDER, DATA_STROKE, STATUS_TOKENS, dataStroke, isDataColorToken, seriesColor, statusColor, hexAlpha, type DataColorToken, type DataColorScheme, type DataColor, type ChipColors, } from './dataColors';
@@ -11,7 +11,7 @@ export { evaluateFormula, formulaReferences, FormulaError, type FormulaValue, ty
11
11
  export { OWNER_PROPERTY_ID, VERIFICATION_PROPERTY_ID, BACKLINKS_PROPERTY_ID, THEME_PROPERTY_ID, COVER_PROPERTY_ID, FONTS_PROPERTY_ID, ICON_PROPERTY_ID, FULLWIDTH_PROPERTY_ID, SYSTEM_PAGE_PROPERTIES, isVerified, verificationExpired, isVerificationActive, makeVerification, extractMentionIds, propertiesReferencePage, type VerificationValue, } from './pageProperties';
12
12
  export { getServerUrlOverride, setServerUrlOverride, getServerTokenOverride, setServerTokenOverride, getIdentityCredential, setIdentityToken, setGuestName, getForwardingAudience, setForwardingAudience, isMixedContentBlocked } from './connection';
13
13
  export { AccountClient, AccountError, DEFAULT_ACCOUNT_URL, getAccountUrlOverride, setAccountUrlOverride, resolveAccountUrl, type AccountSettings, type IdentityTokenResult, } from './account';
14
- export { guestPrincipal, localPrincipal, principalId, verifiedSubject, colorForIdentity, IDENTITY_COLORS, mintIdentityKeypair, signIdentity, decodeIdentity, verifyIdentity, verifyRevocations, signRevocations, principalFromClaims, unverifiedPrincipalFromClaims, LOCAL_OWNER_HEADER, type Principal, type PrincipalKind, type VerifiedVia, type IdentityHeader, type IdentityClaims, type Jwk, type Jwks, type RevocationEntry, type RevocationSet, type IdentityKeypair, type VerifyOptions, type VerifyResult, type VerifyFailure, } from './identity';
14
+ export { guestPrincipal, localPrincipal, principalId, verifiedSubject, colorForIdentity, IDENTITY_COLORS, mintIdentityKeypair, signIdentity, decodeIdentity, verifyIdentity, verifyRevocations, signRevocations, principalFromClaims, unverifiedPrincipalFromClaims, LOCAL_OWNER_HEADER, type Principal, type PrincipalKind, type VerifiedVia, type AgentTokenScope, type AgentTokenMeta, type IdentityHeader, type IdentityClaims, type Jwk, type Jwks, type RevocationEntry, type RevocationSet, type IdentityKeypair, type VerifyOptions, type VerifyResult, type VerifyFailure, } from './identity';
15
15
  export { DEFAULT_INSTANCE_CONFIG, type GuestAccess, type TrustedIssuerConfig, type InstanceConfig, type InstanceInfo, type StoredEdit, type LibraryBinding, type LibraryRoster, type LibraryRosterEntry, } from './provenance';
16
16
  export { authorize, isEmailAuthoritative, type Role, type EffectiveVisibility, type AclEntry, type AccessPage, type AccessCtx, type Decision, } from './authorize';
17
17
  export { snapshotText, paragraphBlocks, textSnapshot, appendTextToSnapshot, appendBlocksToSnapshot, type AppendBlock } from './content';
@@ -29,5 +29,5 @@ export { markdownToBlocks, markdownToImportedDoc, type MarkdownImportOptions } f
29
29
  export { notionExportToImportedDoc, parseCsv } from './notionImport';
30
30
  export { rehydrateImageUrls, rehydrateStoredImages, importedImageBlock, imagePlaceholderMeta, mimeFromRef, notionAssetResolver, urlAssetResolver, isHttpUrl, isDataUrlRef, IMAGE_BLOCK_TYPE, DEFAULT_MAX_ASSET_BYTES, type RehydrateUrlOptions, type RehydrateStoredClient, type RehydrateStoredOptions, type RehydrateStoredResult, type ImportAssetResolver, type AssetBytes, } from './importAssets';
31
31
  export { BACKUP_VERSION, BACKUP_CADENCES, BACKUP_CADENCE_MS, DEFAULT_BACKUP_CONFIG, remapBundle, type LibraryBackup, type ImportMode, type ImportRequest, type ImportResult, type BackupCadence, type BackupConfig, type BackupCadenceStatus, type BackupStatus, } from './backup';
32
- export type { AgentChatEvent, AgentChatMessage, AgentChatOptions, AgentProposal, InterviewStep, AiProvider, AiConfig, AiProviderSettings, AiEffort, AiSkill, AiStatus, AiSearchResult, AiSearchResponse, AiTasksResponse, AiStreamEvent, AiModelPrice, AiPricingTable, AiPricingResponse, AiUsageRow, AiUsageTotals, AiUsageResponse, } from './ai';
32
+ export type { AgentChatEvent, AgentChatMessage, AgentChatOptions, AgentProposal, InterviewStep, AiProvider, AiConfig, AiProviderSettings, AiEffort, AiSkill, AiStatus, AiSearchResult, AiSearchResponse, AiTasksResponse, AiStreamEvent, AiModelPrice, AiPricingTable, AiPricingResponse, AiUsageRow, AiUsageTotals, AiUsageResponse, McpTransport, McpServerConfig, McpClientConfig, McpConfigResponse, McpTestResult, } from './ai';
33
33
  export { providerSettings, isPaidProvider } from './ai';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAC,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAC,GAAG,EAAoC,MAAM,UAAU,CAAC;AAEhE,OAAO,EAAC,cAAc,EAAC,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GAazB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,QAAQ,GAKT,MAAM,cAAc,CAAC;AAkDtB,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,cAAc,EACd,aAAa,EACb,OAAO,EACP,iBAAiB,EACjB,qBAAqB,EACrB,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,cAAc,EACd,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,SAAS,EACT,OAAO,EACP,QAAQ,EACR,WAAW,EACX,WAAW,EACX,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,YAAY,GAGb,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,GAExB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAC,oBAAoB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,YAAY,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,qBAAqB,EAAC,MAAM,cAAc,CAAC;AACpP,OAAO,EACL,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,GAGlB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,cAAc,EACd,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,6BAA6B,EAC7B,kBAAkB,GAcnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,uBAAuB,GASxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,oBAAoB,GAOrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAC,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB,EAAE,sBAAsB,EAAmB,MAAM,WAAW,CAAC;AACtI,OAAO,EACL,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,GAEjB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,4BAA4B,EAC5B,oBAAoB,GAErB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,gBAAgB,GAGjB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,UAAU,EACV,OAAO,EACP,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,GAIlB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,GAKzB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAMlB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAC,mBAAmB,EAAE,kBAAkB,EAAE,oBAAoB,EAAC,MAAM,kBAAkB,CAAC;AAc/F,OAAO,EAAC,cAAc,EAAE,mBAAmB,EAAsC,MAAM,aAAa,CAAC;AACrG,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,sBAAsB,EACtB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EAcxB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAC,gBAAgB,EAAE,qBAAqB,EAA6B,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAC,yBAAyB,EAAE,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AACnE,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,GAOxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,WAAW,GASZ,MAAM,UAAU,CAAC;AAwBlB,OAAO,EAAC,gBAAgB,EAAE,cAAc,EAAC,MAAM,MAAM,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/routes.d.ts CHANGED
@@ -98,6 +98,16 @@ export declare const API: {
98
98
  readonly aiModelDownload: "/api/ai/models/download";
99
99
  /** The agent harness: `POST` `{messages, effort?, thinking?, skills?}` → SSE tool/reasoning/proposal/final events. */
100
100
  readonly agentChat: "/api/agent/chat";
101
+ /**
102
+ * External tools (MCP client) config (admin only): `GET` returns
103
+ * {@link McpConfigResponse} (redacted config + `stdioAllowed`); `PUT`
104
+ * `{enabled, servers}` merges it (write-only auth tokens preserved/replaced/
105
+ * cleared) and returns the redacted result. Managing external tool servers is
106
+ * host command-execution territory — gated by `requireInstanceAdmin`. */
107
+ readonly aiMcp: "/api/ai/mcp";
108
+ /** Dry-run one MCP server config (admin only): `POST` a {@link McpServerConfig}
109
+ * → {@link McpTestResult} (connect + list tools; never returns secrets). */
110
+ readonly aiMcpTest: "/api/ai/mcp/test";
101
111
  /** User-authored prompt/recipe skills: `GET` (list) / `PUT` `{skill}` (upsert). */
102
112
  readonly aiSkills: "/api/ai/skills";
103
113
  /** One skill by name: `DELETE`. */
@@ -191,6 +201,21 @@ export declare const API: {
191
201
  readonly backups: "/api/backups";
192
202
  /** Run a backup immediately: `POST` `{cadence?}` → the written file's name. */
193
203
  readonly backupRun: "/api/backups/run";
204
+ /**
205
+ * Agent Personal-Access-Token management (admin only — `requireInstanceAdmin`;
206
+ * a PAT can never reach this route, both by `requireInstanceAdmin` and the
207
+ * scope-gate). `GET` → `{enabled, tokens}` (redacted list + the `agentApi`
208
+ * on/off state); `PUT` `{enabled}` toggles the dark `agentApi` setting; `POST`
209
+ * `{name, scope?, expiresInDays?}` mints a token (404 while `agentApi` is off) and
210
+ * returns the plaintext exactly ONCE. */
211
+ readonly agentTokens: "/api/agent-tokens";
212
+ /** Revoke one agent token by id: `DELETE`. `true` if one was removed. */
213
+ readonly agentToken: (id: string) => string;
214
+ /**
215
+ * Remote streamable-HTTP MCP transport (AGENT-5 — handler built separately).
216
+ * Declared here so the AGENT-6 scope-gate can allowlist it; harmless until the
217
+ * handler is mounted. */
218
+ readonly mcp: "/api/mcp";
194
219
  };
195
220
  /** Result of a {@link API.compact} run: the database's on-disk size before/after,
196
221
  * in bytes, and how much was reclaimed. */
package/dist/routes.js CHANGED
@@ -100,6 +100,16 @@ export const API = {
100
100
  aiModelDownload: '/api/ai/models/download',
101
101
  /** The agent harness: `POST` `{messages, effort?, thinking?, skills?}` → SSE tool/reasoning/proposal/final events. */
102
102
  agentChat: '/api/agent/chat',
103
+ /**
104
+ * External tools (MCP client) config (admin only): `GET` returns
105
+ * {@link McpConfigResponse} (redacted config + `stdioAllowed`); `PUT`
106
+ * `{enabled, servers}` merges it (write-only auth tokens preserved/replaced/
107
+ * cleared) and returns the redacted result. Managing external tool servers is
108
+ * host command-execution territory — gated by `requireInstanceAdmin`. */
109
+ aiMcp: '/api/ai/mcp',
110
+ /** Dry-run one MCP server config (admin only): `POST` a {@link McpServerConfig}
111
+ * → {@link McpTestResult} (connect + list tools; never returns secrets). */
112
+ aiMcpTest: '/api/ai/mcp/test',
103
113
  /** User-authored prompt/recipe skills: `GET` (list) / `PUT` `{skill}` (upsert). */
104
114
  aiSkills: '/api/ai/skills',
105
115
  /** One skill by name: `DELETE`. */
@@ -199,5 +209,21 @@ export const API = {
199
209
  backups: '/api/backups',
200
210
  /** Run a backup immediately: `POST` `{cadence?}` → the written file's name. */
201
211
  backupRun: '/api/backups/run',
212
+ // ── Agent access: PAT credential management (AGENT-6) ─────────────────────────
213
+ /**
214
+ * Agent Personal-Access-Token management (admin only — `requireInstanceAdmin`;
215
+ * a PAT can never reach this route, both by `requireInstanceAdmin` and the
216
+ * scope-gate). `GET` → `{enabled, tokens}` (redacted list + the `agentApi`
217
+ * on/off state); `PUT` `{enabled}` toggles the dark `agentApi` setting; `POST`
218
+ * `{name, scope?, expiresInDays?}` mints a token (404 while `agentApi` is off) and
219
+ * returns the plaintext exactly ONCE. */
220
+ agentTokens: '/api/agent-tokens',
221
+ /** Revoke one agent token by id: `DELETE`. `true` if one was removed. */
222
+ agentToken: (id) => `/api/agent-tokens/${encodeURIComponent(id)}`,
223
+ /**
224
+ * Remote streamable-HTTP MCP transport (AGENT-5 — handler built separately).
225
+ * Declared here so the AGENT-6 scope-gate can allowlist it; harmless until the
226
+ * handler is mounted. */
227
+ mcp: '/api/mcp',
202
228
  };
203
229
  //# sourceMappingURL=routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,MAAM,EAAE,SAAS;IACjB,kDAAkD;IAClD,KAAK,EAAE,YAAY;IACnB,oFAAoF;IACpF,IAAI,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,EAAE;IACpE,wFAAwF;IACxF,cAAc,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,aAAa;IACzF,+DAA+D;IAC/D,aAAa,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,YAAY;IACvF,wEAAwE;IACxE,WAAW,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,UAAU;IACnF,qFAAqF;IACrF,QAAQ,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,OAAO;IAC7E,kFAAkF;IAClF,aAAa,EAAE,aAAa;IAC5B,4EAA4E;IAC5E,aAAa,EAAE,aAAa;IAC5B,gFAAgF;IAChF,KAAK,EAAE,YAAY;IACnB,8EAA8E;IAC9E,SAAS,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,EAAE;IACzE,iEAAiE;IACjE,MAAM,EAAE,aAAa;IACrB,6DAA6D;IAC7D,UAAU,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,SAAS;IACjF;;;;;;;OAOG;IACH,WAAW,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,UAAU;IACnF;;;;;;;OAOG;IACH,QAAQ,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,OAAO;IAC7E;;;;;;;;;;OAUG;IACH,aAAa,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,YAAY;IACvF;;;;;OAKG;IACH,IAAI,EAAE,WAAW;IAEjB,8EAA8E;IAC9E,8DAA8D;IAC9D,SAAS,EAAE,gBAAgB;IAC3B,mEAAmE;IACnE,QAAQ,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,kBAAkB,kBAAkB,CAAC,EAAE,CAAC,EAAE;IAC5E,4EAA4E;IAC5E,YAAY,EAAE,CAAC,MAAc,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,MAAM,CAAC,WAAW;IAC7F,oEAAoE;IACpE,YAAY,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,kBAAkB,kBAAkB,CAAC,EAAE,CAAC,OAAO;IACrF,sDAAsD;IACtD,iBAAiB,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,kBAAkB,kBAAkB,CAAC,EAAE,CAAC,aAAa;IAChG,mGAAmG;IACnG,WAAW,EAAE,CAAC,EAAU,EAAE,KAAa,EAAU,EAAE,CACjD,kBAAkB,kBAAkB,CAAC,EAAE,CAAC,SAAS,kBAAkB,CAAC,KAAK,CAAC,EAAE;IAC9E,gFAAgF;IAChF,cAAc,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,kBAAkB,kBAAkB,CAAC,EAAE,CAAC,SAAS;IAEzF,8EAA8E;IAC9E,kFAAkF;IAClF,QAAQ,EAAE,gBAAgB;IAC1B,8EAA8E;IAC9E,QAAQ,EAAE,gBAAgB;IAC1B,+CAA+C;IAC/C,OAAO,EAAE,eAAe;IACxB,0EAA0E;IAC1E,QAAQ,EAAE,gBAAgB;IAC1B,yEAAyE;IACzE,UAAU,EAAE,kBAAkB;IAC9B,qEAAqE;IACrE,OAAO,EAAE,eAAe;IACxB,4EAA4E;IAC5E,UAAU,EAAE,kBAAkB;IAC9B,wEAAwE;IACxE,eAAe,EAAE,yBAAyB;IAC1C,sHAAsH;IACtH,SAAS,EAAE,iBAAiB;IAC5B;;;;;6EAKyE;IACzE,KAAK,EAAE,aAAa;IACpB;iFAC6E;IAC7E,SAAS,EAAE,kBAAkB;IAC7B,mFAAmF;IACnF,QAAQ,EAAE,gBAAgB;IAC1B,mCAAmC;IACnC,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,kBAAkB,kBAAkB,CAAC,IAAI,CAAC,EAAE;IACvE;sFACkF;IAClF,SAAS,EAAE,iBAAiB;IAC5B;;mDAE+C;IAC/C,OAAO,EAAE,eAAe;IACxB;6CACyC;IACzC,gBAAgB,EAAE,yBAAyB;IAC3C,OAAO,EAAE,cAAc;IACvB,MAAM,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,gBAAgB,EAAE,EAAE;IAE5C,gFAAgF;IAChF;;;;;;;OAOG;IACH,MAAM,EAAE,aAAa;IACrB;;;;;;OAMG;IACH,KAAK,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,eAAe,kBAAkB,CAAC,EAAE,CAAC,EAAE;IAEtE,gFAAgF;IAChF,uFAAuF;IACvF,WAAW,EAAE,CAAC,MAAc,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,MAAM,CAAC,cAAc;IAC/F,2EAA2E;IAC3E,UAAU,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,oBAAoB,kBAAkB,CAAC,EAAE,CAAC,EAAE;IAChF,mGAAmG;IACnG,QAAQ,EAAE,CAAC,MAAc,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,MAAM,CAAC,WAAW;IACzF,kCAAkC;IAClC,OAAO,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,iBAAiB,kBAAkB,CAAC,EAAE,CAAC,EAAE;IAE1E;;;;OAIG;IACH,OAAO,EAAE,0BAA0B;IAEnC,gFAAgF;IAChF;;;;OAIG;IACH,QAAQ,EAAE,eAAe;IACzB,sEAAsE;IACtE,SAAS,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,QAAQ;IAE/E,gFAAgF;IAChF;;;;OAIG;IACH,OAAO,EAAE,cAAc;IACvB,sEAAsE;IACtE,MAAM,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,gBAAgB,kBAAkB,CAAC,EAAE,CAAC,EAAE;IACxE;;;;OAIG;IACH,OAAO,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,MAAM;IAC3E;;;;OAIG;IACH,cAAc,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,cAAc,kBAAkB,CAAC,EAAE,CAAC,aAAa;IAEzF,gFAAgF;IAChF;;;;;OAKG;IACH,WAAW,EAAE,mBAAmB;IAChC;;;;OAIG;IACH,aAAa,EAAE,qBAAqB;IAEpC,gFAAgF;IAChF;iDAC6C;IAC7C,OAAO,EAAE,cAAc;IACvB,+EAA+E;IAC/E,SAAS,EAAE,kBAAkB;IAE7B,iFAAiF;IACjF;;;;;;6CAMyC;IACzC,WAAW,EAAE,mBAAmB;IAChC,yEAAyE;IACzE,UAAU,EAAE,CAAC,EAAU,EAAU,EAAE,CAAC,qBAAqB,kBAAkB,CAAC,EAAE,CAAC,EAAE;IACjF;;;6BAGyB;IACzB,GAAG,EAAE,UAAU;CACP,CAAC"}
@@ -28,7 +28,7 @@ import type { StoredPage } from './types';
28
28
  export type TemplateTag = 'interactive' | 'slides' | 'database';
29
29
  export interface PageTemplate {
30
30
  /** Stable identifier (i18n keys + tests hang off this). */
31
- id: 'grocery-tracker' | 'task-board' | 'reading-list' | 'project-intake' | 'savings-planner' | 'roadmap' | 'field-map' | 'pitch-deck' | 'compound-growth' | 'team-status' | 'product-hq';
31
+ id: 'grocery-tracker' | 'task-board' | 'reading-list' | 'project-intake' | 'savings-planner' | 'roadmap' | 'field-map' | 'pitch-deck' | 'compound-growth' | 'team-status' | 'product-hq' | 'dashboard';
32
32
  /** Emoji shown on the gallery card and applied to the created page. */
33
33
  icon: string;
34
34
  /** Canonical (English) page name; suffixed when it collides. */
package/dist/templates.js CHANGED
@@ -18,6 +18,7 @@ const GUIDANCE = {
18
18
  roadmap: 'This template shows swimlanes: the Timeline bands and the Board lanes both split by Area. Try it: drag a bar to reschedule, collapse a lane, or move a card between stages.',
19
19
  fieldMap: 'This template shows a map database: rows with a location render as region-coloured pins, and the address-only row waits under Unplaced. Try it: click a pin, geocode the unplaced row, or switch to the Table view.',
20
20
  productHq: 'This template shows two linked databases: each initiative relates to tasks on the Tasks sub-page, and the Progress and Task count columns roll those tasks up. Try it: tick a task done on the sub-page and watch the rollup move, or open the Tasks timeline for the dependency arrows.',
21
+ dashboard: 'This dashboard reads a sample sales database: the KPI tiles total the rows, and the bar, pie and trend charts group them — all live. Try it: pick a quarter in the control at the top and every tile and chart re-scopes to it at once — or edit a deal on the “… data” sub-page and watch a tile move.',
21
22
  };
22
23
  /** The guidance callout as a block-doc block (ids are stable per template). */
23
24
  const guidanceCallout = (id, text) => ({
@@ -755,6 +756,191 @@ const createProductHq = async (client, name, guidance = GUIDANCE.productHq) => {
755
756
  await client.updateRow(initiativesDbId, billing, { properties: { p_status: 'opt_shipped', p_tasks: [t5] } });
756
757
  return page;
757
758
  };
759
+ // ── 📊 Dashboard ─────────────────────────────────────────────────────────────
760
+ // A composite dashboard: a KPI row + DB-backed bar/pie/trend charts laid out in
761
+ // the 12-col column blocks, all reading LIVE from a sample "sales" database the
762
+ // template also seeds (a sub-page). The charts are ordinary in-doc `kitchart`
763
+ // blocks in DATABASE source mode (DASH-3) — their `dbId` is stamped at
764
+ // instantiation, when the sample database's id is minted. Unlike the database
765
+ // fixtures you land on a composed DOCUMENT, not a table/board.
766
+ /** The sample sales database the dashboard charts. One row = one deal, with a
767
+ * Region/Channel/Stage/Quarter to group by and Amount/Units to total. */
768
+ const SALES_SCHEMA = {
769
+ properties: [
770
+ {
771
+ id: 'p_region',
772
+ name: 'Region',
773
+ type: 'select',
774
+ options: [
775
+ { id: 'opt_north', label: 'North', color: 'blue' },
776
+ { id: 'opt_south', label: 'South', color: 'green' },
777
+ { id: 'opt_east', label: 'East', color: 'orange' },
778
+ { id: 'opt_west', label: 'West', color: 'purple' },
779
+ ],
780
+ },
781
+ {
782
+ id: 'p_channel',
783
+ name: 'Channel',
784
+ type: 'select',
785
+ options: [
786
+ { id: 'opt_online', label: 'Online', color: 'blue' },
787
+ { id: 'opt_retail', label: 'Retail', color: 'pink' },
788
+ { id: 'opt_partner', label: 'Partner', color: 'yellow' },
789
+ ],
790
+ },
791
+ {
792
+ id: 'p_stage',
793
+ name: 'Stage',
794
+ type: 'status',
795
+ options: [
796
+ { id: 'opt_pipeline', label: 'Pipeline', color: 'gray', group: 'todo' },
797
+ { id: 'opt_committed', label: 'Committed', color: 'blue', group: 'in_progress' },
798
+ { id: 'opt_won', label: 'Won', color: 'green', group: 'complete' },
799
+ ],
800
+ },
801
+ {
802
+ id: 'p_quarter',
803
+ name: 'Quarter',
804
+ type: 'select',
805
+ options: [
806
+ { id: 'opt_q1', label: 'Q1', color: 'gray' },
807
+ { id: 'opt_q2', label: 'Q2', color: 'gray' },
808
+ { id: 'opt_q3', label: 'Q3', color: 'gray' },
809
+ { id: 'opt_q4', label: 'Q4', color: 'gray' },
810
+ ],
811
+ },
812
+ { id: 'p_amount', name: 'Amount', type: 'number', numberDisplay: 'number' },
813
+ { id: 'p_units', name: 'Units', type: 'number' },
814
+ ],
815
+ views: [
816
+ // The data page opens on the table; a bar view backs it (and exercises the
817
+ // DB chart-view kit engine on the sample data itself).
818
+ { id: 'v_table', name: 'Table', type: 'table', filters: [], sorts: [] },
819
+ { id: 'v_bar', name: 'By region', type: 'bar', filters: [], sorts: [], groupByPropertyId: 'p_region', aggregate: { type: 'sum', propertyId: 'p_amount' } },
820
+ ],
821
+ };
822
+ const SALES_ROWS = [
823
+ { name: 'Northwind renewal', properties: { p_region: 'opt_north', p_channel: 'opt_online', p_stage: 'opt_won', p_quarter: 'opt_q1', p_amount: 24000, p_units: 120 } },
824
+ { name: 'Acme retail order', properties: { p_region: 'opt_north', p_channel: 'opt_retail', p_stage: 'opt_committed', p_quarter: 'opt_q2', p_amount: 18000, p_units: 90 } },
825
+ { name: 'Globex expansion', properties: { p_region: 'opt_south', p_channel: 'opt_online', p_stage: 'opt_won', p_quarter: 'opt_q1', p_amount: 31000, p_units: 150 } },
826
+ { name: 'Initech pilot', properties: { p_region: 'opt_south', p_channel: 'opt_partner', p_stage: 'opt_pipeline', p_quarter: 'opt_q3', p_amount: 12000, p_units: 60 } },
827
+ { name: 'Umbrella upsell', properties: { p_region: 'opt_east', p_channel: 'opt_online', p_stage: 'opt_committed', p_quarter: 'opt_q2', p_amount: 27000, p_units: 130 } },
828
+ { name: 'Soylent reorder', properties: { p_region: 'opt_east', p_channel: 'opt_retail', p_stage: 'opt_won', p_quarter: 'opt_q4', p_amount: 22000, p_units: 110 } },
829
+ { name: 'Hooli trial', properties: { p_region: 'opt_west', p_channel: 'opt_partner', p_stage: 'opt_pipeline', p_quarter: 'opt_q3', p_amount: 9000, p_units: 45 } },
830
+ { name: 'Stark contract', properties: { p_region: 'opt_west', p_channel: 'opt_online', p_stage: 'opt_won', p_quarter: 'opt_q4', p_amount: 35000, p_units: 170 } },
831
+ { name: 'Wayne partnership', properties: { p_region: 'opt_north', p_channel: 'opt_partner', p_stage: 'opt_committed', p_quarter: 'opt_q2', p_amount: 15000, p_units: 70 } },
832
+ { name: 'Cyberdyne restock', properties: { p_region: 'opt_south', p_channel: 'opt_retail', p_stage: 'opt_won', p_quarter: 'opt_q1', p_amount: 20000, p_units: 100 } },
833
+ { name: 'Tyrell evaluation', properties: { p_region: 'opt_east', p_channel: 'opt_online', p_stage: 'opt_pipeline', p_quarter: 'opt_q3', p_amount: 14000, p_units: 68 } },
834
+ { name: 'Massive Dynamic order', properties: { p_region: 'opt_west', p_channel: 'opt_retail', p_stage: 'opt_committed', p_quarter: 'opt_q4', p_amount: 17000, p_units: 82 } },
835
+ ];
836
+ /** A database-bound `kitchart` block (DASH-3 source mode). `dbId` is stamped at
837
+ * instantiation; `count` needs no numeric property, so `aggProp` is optional. A
838
+ * one-line `description` keeps the edit view clean (no ghost "Add a description…"). */
839
+ const dashboardChart = (dbId, id, kind, title, description, groupBy, aggType, aggProp,
840
+ /** Cross-filter (DASH-7): bind the chart to a named input + a property so a
841
+ * top-of-dashboard control re-scopes it. Omitted → the chart is unfiltered. */
842
+ filter) => ({
843
+ id,
844
+ type: 'kitchart',
845
+ props: {
846
+ kind,
847
+ title,
848
+ description,
849
+ sourceMode: 'database',
850
+ dbId,
851
+ dbGroupBy: groupBy,
852
+ dbAggType: aggType,
853
+ ...(aggProp ? { dbAggProp: aggProp } : {}),
854
+ ...(filter ? { dbFilterInput: filter.input, dbFilterProp: filter.prop } : {}),
855
+ },
856
+ });
857
+ /** The dashboard document: a leading guidance callout, a KPI row across the top
858
+ * (three tiles in a 12-col columns block), then the bar+pie pair and a
859
+ * full-width quarterly trend — every chart bound to the seeded sales database. */
860
+ const dashboardBlocks = (dbId, guidance) => {
861
+ // The cross-filter (DASH-7): a Quarter control published as `quarter`. Every
862
+ // chart below (except the quarterly trend, which IS the quarter axis) binds to
863
+ // it on the Quarter property, so picking a quarter re-scopes the whole board.
864
+ const QUARTER_FILTER = { input: 'quarter', prop: 'p_quarter' };
865
+ const chart = (id, kind, title, description, groupBy, aggType, aggProp, filter = QUARTER_FILTER) => dashboardChart(dbId, id, kind, title, description, groupBy, aggType, aggProp, filter ?? undefined);
866
+ return [
867
+ guidanceCallout('db-guide', guidance),
868
+ // Cross-filter control: a dropdown that publishes `quarter`. "All quarters"
869
+ // (value `all`) is the default and reads as inactive, so the board opens
870
+ // showing the whole year; picking Q1–Q4 scopes every bound chart at once.
871
+ {
872
+ id: 'db-filter',
873
+ type: 'dropdown',
874
+ props: {
875
+ name: 'quarter',
876
+ label: 'Quarter',
877
+ value: 'all',
878
+ opts: [{ label: 'All quarters', value: 'all' }, { label: 'Q1' }, { label: 'Q2' }, { label: 'Q3' }, { label: 'Q4' }],
879
+ },
880
+ },
881
+ { id: 'db-h1', type: 'heading', text: [{ t: 'This quarter at a glance' }], props: { level: 2 } },
882
+ // KPI row — three number tiles across the top (the DASH-5 `kpi` kind). Each
883
+ // folds a grouped DB series to one grand total; the tile's title names it.
884
+ // All three bind to the Quarter cross-filter (default `chart` filter).
885
+ {
886
+ id: 'db-kpis',
887
+ type: 'columns',
888
+ children: [
889
+ { id: 'db-kc1', type: 'column', props: { span: 4 }, children: [chart('db-k-rev', 'kpi', 'Total revenue (£)', 'Summed across every region', 'p_region', 'sum', 'p_amount')] },
890
+ { id: 'db-kc2', type: 'column', props: { span: 4 }, children: [chart('db-k-deals', 'kpi', 'Deals', 'Every row, across all stages', 'p_stage', 'count')] },
891
+ { id: 'db-kc3', type: 'column', props: { span: 4 }, children: [chart('db-k-units', 'kpi', 'Units sold', 'Summed across every channel', 'p_channel', 'sum', 'p_units')] },
892
+ ],
893
+ },
894
+ { id: 'db-h2', type: 'heading', text: [{ t: 'Breakdown' }], props: { level: 2 } },
895
+ // Bar + pie side by side in a 6/6 split — both scoped by the Quarter filter.
896
+ {
897
+ id: 'db-cols',
898
+ type: 'columns',
899
+ children: [
900
+ { id: 'db-cl', type: 'column', props: { span: 6 }, children: [chart('db-bar', 'bar', 'Revenue by region (£)', 'Amount summed per sales region', 'p_region', 'sum', 'p_amount')] },
901
+ { id: 'db-cr', type: 'column', props: { span: 6 }, children: [chart('db-pie', 'pie', 'Deals by channel', 'Share of deals won online, retail and via partners', 'p_channel', 'count')] },
902
+ ],
903
+ },
904
+ // A full-width quarterly trend closes the dashboard. It is NOT bound to the
905
+ // Quarter filter (it is already the quarter axis) — it stays full-year, giving
906
+ // context while the tiles + breakdowns above slice to the chosen quarter.
907
+ chart('db-line', 'line', 'Revenue by quarter (£)', 'Amount summed by quarter, Q1 → Q4', 'p_quarter', 'sum', 'p_amount', null),
908
+ // Trailing pointer: how to ADD a chart (distinct from the lead callout, which
909
+ // is about editing the data). The slash menu inserts a Chart, then its ⚙
910
+ // Source toggle switches it to Database — there is no "/chart → Database".
911
+ { id: 'db-note', type: 'callout', text: [{ t: 'Want another cut of the data? Type /chart to insert one, then switch its Source to Database in the chart’s ⚙ settings and pick this database.' }], props: { variant: 'info' } },
912
+ ];
913
+ };
914
+ /** Build the dashboard document, then seed the sample sales database it charts.
915
+ * The dashboard host page is saved FIRST (so its charts' `dbId` is the minted
916
+ * sample-db id), and the sample database lands on a sub-page. */
917
+ const createDashboard = async (client, name, guidance = GUIDANCE.dashboard) => {
918
+ const dataDbId = globalThis.crypto.randomUUID();
919
+ const dataName = `${name} data`;
920
+ const page = await client.savePage({
921
+ name,
922
+ data: { editorjs: { blocks: [] }, values: [], names: [], editor: 'blocks', blockdoc: { blocks: dashboardBlocks(dataDbId, guidance) } },
923
+ });
924
+ const dataPage = await client.savePage({ name: dataName, data: emptySnapshot([]), parentId: page.id });
925
+ await client.createDatabase({ id: dataDbId, pageId: dataPage.id, name: dataName, schema: SALES_SCHEMA });
926
+ for (const row of SALES_ROWS) {
927
+ let rowName = row.name;
928
+ for (let attempt = 2;; attempt += 1) {
929
+ try {
930
+ await client.createRow(dataDbId, { ...row, name: rowName });
931
+ break;
932
+ }
933
+ catch {
934
+ if (attempt > 5) {
935
+ await client.createRow(dataDbId, { ...row, name: null });
936
+ break;
937
+ }
938
+ rowName = `${row.name} ${attempt}`;
939
+ }
940
+ }
941
+ }
942
+ return page;
943
+ };
758
944
  // ── The gallery ──────────────────────────────────────────────────────────────
759
945
  /** Create a block-editor template page from a JSON block projection. */
760
946
  const createBlockDocPage = (blocks) => (client, name) => client.savePage({ name, data: { editorjs: { blocks: [] }, values: [], names: [], editor: 'blocks', blockdoc: { blocks } } });
@@ -803,6 +989,10 @@ export const PAGE_TEMPLATES = [
803
989
  { id: 'pitch-deck', icon: '📽️', pageName: 'Pitch deck', tags: ['interactive', 'slides'], create: createBlockDocPage(PITCH_DECK_BLOCKS) },
804
990
  { id: 'team-status', icon: '🚦', pageName: 'Team status dashboard', tags: ['interactive'], create: createBlockDocPage(TEAM_STATUS_BLOCKS) },
805
991
  { id: 'product-hq', icon: '🎯', pageName: 'Product HQ', tags: ['database'], guidance: GUIDANCE.productHq, create: createProductHq },
992
+ // A composite dashboard: KPI tiles + DB-backed charts over a seeded sales
993
+ // database. Tagged `interactive` (you land on a document, not a table), so it
994
+ // groups under Interactive documents; the description names its data backing.
995
+ { id: 'dashboard', icon: '📊', pageName: 'Sales dashboard', tags: ['interactive'], guidance: GUIDANCE.dashboard, create: createDashboard },
806
996
  // The classic sample document, folded into the gallery. Unlike the Home
807
997
  // starter's open-or-create (which targets the canonical sample name and never
808
998
  // overwrites), the gallery card always mints a FRESH copy under its own