@k2b/cloud 0.7.0 → 0.8.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/package.json +5 -4
- package/scripts/README.md +10 -0
- package/scripts/build.ts +6 -1
- package/scripts/preload.ts +7 -0
- package/scripts/sync-recovery-smoke.ts +21 -6
- package/src/_internal/build-metadata.ts +8 -1
- package/src/_internal/capabilities.ts +35 -12
- package/src/_internal/define-app.ts +28 -11
- package/src/_internal/page-responses.ts +1 -1
- package/src/_internal/readiness.ts +4 -1
- package/src/_internal/runtime-context.ts +1 -1
- package/src/access/PermissionEditor.tsx +15 -9
- package/src/access/PrincipalPicker.tsx +9 -3
- package/src/access/ResourceApiKeys.tsx +1 -1
- package/src/access/ui.ts +1 -2
- package/src/ai/admin.ts +2 -2
- package/src/ai/assistant-models.ts +1 -1
- package/src/ai/audio-tool.ts +1 -1
- package/src/ai/browser-code-contracts.ts +174 -52
- package/src/ai/browser.ts +4 -2
- package/src/ai/capabilities.ts +3 -3
- package/src/ai/chat/blocks.tsx +127 -50
- package/src/ai/chat/builtin-tools.tsx +2 -4
- package/src/ai/chat/capability-table.tsx +1 -1
- package/src/ai/chat/composer-adapter.ts +48 -13
- package/src/ai/chat/message-utils.ts +18 -5
- package/src/ai/chat/presentation.tsx +24 -11
- package/src/ai/chat/tool-disclosure.tsx +6 -2
- package/src/ai/chat/tool-groups.ts +8 -2
- package/src/ai/chat-quotas.ts +2 -2
- package/src/ai/chat-task-contracts.ts +9 -13
- package/src/ai/chat-tasks.ts +39 -18
- package/src/ai/client/controller.ts +87 -38
- package/src/ai/client/projection.ts +13 -6
- package/src/ai/client/transport.ts +4 -2
- package/src/ai/code-capability-routes.ts +22 -27
- package/src/ai/code-capability-transport.ts +2 -1
- package/src/ai/code-execution.ts +53 -0
- package/src/ai/code-mode-skill.ts +9 -5
- package/src/ai/code-runtime-tools.ts +16 -3
- package/src/ai/code-source-contracts.ts +100 -32
- package/src/ai/code-source-tools.ts +49 -6
- package/src/ai/compaction.ts +3 -3
- package/src/ai/default-tools.ts +87 -14
- package/src/ai/dictation-runtime.ts +1 -1
- package/src/ai/draft-content.ts +27 -12
- package/src/ai/enrich.ts +1 -1
- package/src/ai/executor.ts +8 -12
- package/src/ai/fetch-file-tool.ts +2 -2
- package/src/ai/file-content-version.ts +6 -1
- package/src/ai/file-tools.ts +8 -2
- package/src/ai/files-store.ts +55 -17
- package/src/ai/grids-skill.ts +2 -0
- package/src/ai/http.ts +18 -7
- package/src/ai/index.ts +24 -25
- package/src/ai/inference-calls.ts +3 -4
- package/src/ai/live.ts +1 -1
- package/src/ai/markdown-pdf-tool.ts +2 -2
- package/src/ai/memories.ts +30 -27
- package/src/ai/memory-learning-runs.ts +1 -1
- package/src/ai/memory-learning.ts +5 -5
- package/src/ai/memory-tool.ts +1 -5
- package/src/ai/memory-workflow-evidence.ts +9 -4
- package/src/ai/message-queue.ts +23 -12
- package/src/ai/migrate.ts +6 -4
- package/src/ai/model-pricing.ts +4 -4
- package/src/ai/pdf-render-worker.ts +3 -2
- package/src/ai/projects-routes.ts +15 -40
- package/src/ai/projects.ts +18 -13
- package/src/ai/quota-provider.ts +6 -5
- package/src/ai/quota-report.ts +5 -5
- package/src/ai/quotas.ts +3 -3
- package/src/ai/routes.ts +60 -34
- package/src/ai/run-timeout.ts +3 -1
- package/src/ai/runtime-tools.ts +6 -7
- package/src/ai/runtime.ts +38 -20
- package/src/ai/settings.ts +1 -1
- package/src/ai/short-id.ts +4 -5
- package/src/ai/skill-search.ts +21 -10
- package/src/ai/skill-seeds.ts +16 -7
- package/src/ai/skill-tool.ts +6 -1
- package/src/ai/skills-routes.ts +32 -15
- package/src/ai/skills.ts +54 -28
- package/src/ai/store.ts +80 -47
- package/src/ai/structured.ts +6 -2
- package/src/ai/system-prompt.ts +7 -5
- package/src/ai/task-contracts.ts +84 -0
- package/src/ai/task-execution.ts +92 -0
- package/src/ai/todo-contracts.ts +24 -12
- package/src/ai/todo-tool.ts +16 -13
- package/src/ai/tools.ts +1 -1
- package/src/ai/transcription.ts +4 -4
- package/src/ai/turn-timing.ts +1 -1
- package/src/ai/ui.tsx +2 -1
- package/src/ai/usage.ts +1 -1
- package/src/ai/vision-tool.ts +38 -17
- package/src/api/admin-ai-quotas.ts +14 -8
- package/src/api/admin-ai-skills.ts +3 -3
- package/src/api/admin-linux-identities.ts +10 -4
- package/src/api/admin-rail.ts +2 -2
- package/src/api/app-approval.ts +2 -2
- package/src/api/auth.ts +1 -1
- package/src/api/capabilities.ts +45 -13
- package/src/api/index.ts +5 -5
- package/src/api/mcp.ts +5 -12
- package/src/browser/CloudResourceSearch.tsx +1 -1
- package/src/browser/command-bridge.ts +1 -1
- package/src/browser/command-shortcuts.ts +1 -1
- package/src/browser/commands.ts +8 -7
- package/src/browser/testing.ts +2 -1
- package/src/capabilities/client.ts +1 -1
- package/src/capabilities/command-link.ts +1 -1
- package/src/capabilities/server.ts +2 -1
- package/src/cli/admin/ai-quotas.ts +5 -5
- package/src/cli/admin/app-credentials.ts +7 -9
- package/src/cli/admin/index.ts +3 -3
- package/src/cli/admin/instance.ts +2 -2
- package/src/cli/admin/linux.ts +4 -2
- package/src/cli/admin/sync.ts +1 -1
- package/src/cli/capabilities.ts +42 -29
- package/src/config/define-env.ts +97 -0
- package/src/config/env.ts +147 -54
- package/src/config/index.ts +3 -1
- package/src/contracts/capabilities.ts +8 -1
- package/src/contracts/index.ts +5 -7
- package/src/contracts/registry.ts +2 -0
- package/src/contracts/shared.ts +12 -3
- package/src/desktop/index.ts +1 -0
- package/src/desktop/solid.tsx +1 -1
- package/src/index.ts +8 -2
- package/src/server/index.ts +2 -2
- package/src/server/ratelimit.ts +1 -1
- package/src/server/services/access-revision.ts +3 -3
- package/src/server/services/access.ts +3 -1
- package/src/services/accounts/notification-sender.ts +12 -2
- package/src/services/announcements/index.ts +4 -4
- package/src/services/app-approval.ts +11 -10
- package/src/services/identity/key-config.ts +6 -4
- package/src/services/identity/runtime-config.ts +6 -5
- package/src/services/ipa/posix.ts +1 -1
- package/src/services/ipa/sync.ts +1 -1
- package/src/services/logging/index.ts +1 -1
- package/src/services/logging/trace.ts +1 -1
- package/src/services/mandates/index.ts +5 -2
- package/src/services/mandates/policy.ts +106 -6
- package/src/services/notifications/batches.ts +1 -0
- package/src/services/pdf/gotenberg.ts +50 -14
- package/src/services/pdf/index.ts +11 -11
- package/src/services/public-http.ts +1 -1
- package/src/services/rail-shortcuts.ts +1 -1
- package/src/services/rail-snapshot.ts +1 -1
- package/src/services/request-cache-redis.ts +2 -1
- package/src/services/session/user.ts +1 -1
- package/src/services/settings/app.ts +5 -1
- package/src/services/settings/core-settings.ts +6 -8
- package/src/services/settings/index.ts +3 -3
- package/src/services/settings/store.ts +3 -3
- package/src/services/webauthn.ts +1 -1
- package/src/shared/index.ts +3 -6
- package/src/shared/markdown/extensions/info-blocks.ts +1 -1
- package/src/ssr/AdminSidebar.tsx +2 -2
- package/src/ssr/AppLaunchpad.island.tsx +12 -2
- package/src/ssr/AppLaunchpadPanel.tsx +4 -4
- package/src/ssr/GlobalSearchDialog.tsx +6 -6
- package/src/ssr/GlobalSearchTrigger.island.tsx +2 -2
- package/src/ssr/Layout.tsx +2 -2
- package/src/ssr/LayoutHeader.tsx +1 -1
- package/src/ssr/LayoutHelp.tsx +4 -6
- package/src/ssr/LayoutHelpTrigger.island.tsx +2 -2
- package/src/ssr/MinimalLayout.tsx +1 -1
- package/src/ssr/MobileNavigation.tsx +2 -2
- package/src/ssr/ProfilePreferences.island.tsx +1 -1
- package/src/ssr/admin-navigation.ts +6 -2
- package/src/ssr/islands/index.ts +2 -2
- package/src/ssr/mobile-menu-history.ts +1 -1
- package/src/workflows/ai/index.ts +2 -2
- package/src/workflows/ai/runtime.ts +16 -91
- package/src/workflows/ai/store.ts +1 -1
- package/src/workflows/ai/types.ts +6 -82
- package/src/workflows/store/index.ts +2 -2
- package/src/server/services/freeipa/test-certificates.ts +0 -44
- package/src/services/session/test-fixture.ts +0 -18
|
@@ -12,6 +12,8 @@ const identifierList = <T extends z.ZodType<string>>(schema: T) =>
|
|
|
12
12
|
|
|
13
13
|
const operationSchema = z.union([
|
|
14
14
|
z.literal("search.query"),
|
|
15
|
+
z.literal("runtime.http"),
|
|
16
|
+
z.literal("runtime.database"),
|
|
15
17
|
z.string().refine((value) => {
|
|
16
18
|
const [prefix, localId, extra] = value.split(":");
|
|
17
19
|
return (
|
|
@@ -36,6 +38,79 @@ export const CapabilityGrantSchema = z
|
|
|
36
38
|
export const CapabilityGrantsSchema = z.array(CapabilityGrantSchema).max(MANDATE_POLICY_MAX_IDENTIFIERS / 2);
|
|
37
39
|
export type CapabilityGrant = z.output<typeof CapabilityGrantSchema>;
|
|
38
40
|
|
|
41
|
+
const httpsUrl = z
|
|
42
|
+
.string()
|
|
43
|
+
.url()
|
|
44
|
+
.max(2000)
|
|
45
|
+
.refine((value) => {
|
|
46
|
+
const url = new URL(value);
|
|
47
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.hash;
|
|
48
|
+
}, "Use HTTPS without credentials or fragment");
|
|
49
|
+
export const TaskGrantSchema = z.union([
|
|
50
|
+
CapabilityGrantSchema,
|
|
51
|
+
z
|
|
52
|
+
.object({
|
|
53
|
+
kind: z.literal("http").describe("Authorize scheduled Code Mode HTTP requests."),
|
|
54
|
+
fixedInput: z
|
|
55
|
+
.object({
|
|
56
|
+
origin: httpsUrl
|
|
57
|
+
.refine((value) => new URL(value).origin === value, "Use an exact origin without path")
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Exact HTTPS origin, without path, wildcard or trailing slash."),
|
|
60
|
+
url: httpsUrl.optional().describe("Exact full URL, including query parameters."),
|
|
61
|
+
method: z
|
|
62
|
+
.enum(["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
|
|
63
|
+
.optional()
|
|
64
|
+
.describe("Exact HTTP method; omit to permit every supported method."),
|
|
65
|
+
})
|
|
66
|
+
.strict()
|
|
67
|
+
.default({})
|
|
68
|
+
.describe("Fixed input values. Omitted fields are unrestricted; empty input permits every supported target and operation."),
|
|
69
|
+
})
|
|
70
|
+
.strict(),
|
|
71
|
+
z
|
|
72
|
+
.object({
|
|
73
|
+
kind: z.literal("database").describe("Authorize scheduled Code Mode RSQL operations."),
|
|
74
|
+
fixedInput: z
|
|
75
|
+
.object({
|
|
76
|
+
resourceId: z.string().min(1).max(80).optional().describe("Exact Studio app ID whose database may be used."),
|
|
77
|
+
operation: z
|
|
78
|
+
.enum([
|
|
79
|
+
"connect",
|
|
80
|
+
"status",
|
|
81
|
+
"export",
|
|
82
|
+
"clear",
|
|
83
|
+
"reset",
|
|
84
|
+
"tables.list",
|
|
85
|
+
"tables.create",
|
|
86
|
+
"tables.update",
|
|
87
|
+
"tables.delete",
|
|
88
|
+
"schema.get",
|
|
89
|
+
"rows.list",
|
|
90
|
+
"rows.get",
|
|
91
|
+
"rows.insert",
|
|
92
|
+
"rows.update",
|
|
93
|
+
"rows.delete",
|
|
94
|
+
"query",
|
|
95
|
+
])
|
|
96
|
+
.optional()
|
|
97
|
+
.describe("Exact database operation; connect needs its own grant when operation is fixed."),
|
|
98
|
+
table: z
|
|
99
|
+
.string()
|
|
100
|
+
.min(1)
|
|
101
|
+
.max(63)
|
|
102
|
+
.optional()
|
|
103
|
+
.describe("Exact table; applies to row and schema operations, not arbitrary SQL queries."),
|
|
104
|
+
})
|
|
105
|
+
.strict()
|
|
106
|
+
.default({})
|
|
107
|
+
.describe("Fixed input values. Omitted fields are unrestricted; empty input permits every supported target and operation."),
|
|
108
|
+
})
|
|
109
|
+
.strict(),
|
|
110
|
+
]);
|
|
111
|
+
export const TaskGrantsSchema = z.array(TaskGrantSchema).max(MANDATE_POLICY_MAX_IDENTIFIERS / 2);
|
|
112
|
+
export type TaskGrant = z.output<typeof TaskGrantSchema>;
|
|
113
|
+
|
|
39
114
|
const equalJson = (a: unknown, b: unknown): boolean => {
|
|
40
115
|
if (a === b) return true;
|
|
41
116
|
if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
|
|
@@ -66,7 +141,7 @@ export const MandatePolicyV1Schema = z
|
|
|
66
141
|
apps: z.union([z.literal("*"), identifierList(CapabilityAppIdSchema)]),
|
|
67
142
|
operations: z.union([z.literal("*"), identifierList(operationSchema)]),
|
|
68
143
|
actions: z.enum(["deny", "require_approval", "preapproved"]),
|
|
69
|
-
grants:
|
|
144
|
+
grants: TaskGrantsSchema.optional(),
|
|
70
145
|
})
|
|
71
146
|
.strict()
|
|
72
147
|
.superRefine((policy, context) => {
|
|
@@ -107,11 +182,17 @@ export const isMandatePolicyNarrowing = (current: MandatePolicyV1, next: Mandate
|
|
|
107
182
|
setAllows(current.apps, next.apps) &&
|
|
108
183
|
setAllows(current.operations, next.operations) &&
|
|
109
184
|
ACTION_AUTHORITY[next.actions] <= ACTION_AUTHORITY[current.actions] &&
|
|
110
|
-
(current.grants === undefined
|
|
111
|
-
|
|
185
|
+
(current.grants === undefined
|
|
186
|
+
? !next.grants?.some((grant) => grant.kind === "http" || grant.kind === "database")
|
|
187
|
+
: next.grants !== undefined &&
|
|
112
188
|
next.grants.every((grant) =>
|
|
113
|
-
current.grants!.some((existing) =>
|
|
114
|
-
|
|
189
|
+
current.grants!.some((existing) => {
|
|
190
|
+
if (existing.kind !== grant.kind) return false;
|
|
191
|
+
if ("appId" in existing && "appId" in grant && (existing.appId !== grant.appId || existing.capabilityId !== grant.capabilityId))
|
|
192
|
+
return false;
|
|
193
|
+
return Object.entries(existing.fixedInput).every(([key, value]) => equalJson(value, Reflect.get(grant.fixedInput, key)));
|
|
194
|
+
}),
|
|
195
|
+
));
|
|
115
196
|
|
|
116
197
|
export const mandatePolicyAllows = (
|
|
117
198
|
policy: MandatePolicyV1,
|
|
@@ -125,6 +206,23 @@ export const mandatePolicyAllows = (
|
|
|
125
206
|
): boolean => {
|
|
126
207
|
if (policy.apps !== "*" && !policy.apps.includes(input.appId)) return false;
|
|
127
208
|
if (policy.operations !== "*" && !policy.operations.includes(input.operation)) return false;
|
|
209
|
+
if (input.operation === "runtime.http" || input.operation === "runtime.database") {
|
|
210
|
+
const kind = input.operation === "runtime.http" ? "http" : "database";
|
|
211
|
+
return (
|
|
212
|
+
input.appId === "assistant" &&
|
|
213
|
+
!!policy.grants?.some(
|
|
214
|
+
(grant) =>
|
|
215
|
+
grant.kind === kind &&
|
|
216
|
+
Object.entries(grant.fixedInput).every(
|
|
217
|
+
([key, value]) =>
|
|
218
|
+
input.input !== null &&
|
|
219
|
+
typeof input.input === "object" &&
|
|
220
|
+
Object.hasOwn(input.input, key) &&
|
|
221
|
+
equalJson(value, Reflect.get(input.input, key)),
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
128
226
|
if (policy.grants !== undefined) {
|
|
129
227
|
const [operation, capabilityId] = input.operation.split(":");
|
|
130
228
|
const kind =
|
|
@@ -136,7 +234,9 @@ export const mandatePolicyAllows = (
|
|
|
136
234
|
if (
|
|
137
235
|
!kind ||
|
|
138
236
|
!capabilityId ||
|
|
139
|
-
!policy.grants.some(
|
|
237
|
+
!policy.grants.some(
|
|
238
|
+
(grant) => "appId" in grant && capabilityGrantAllows(grant, { appId: input.appId, capabilityId, kind, input: input.input }),
|
|
239
|
+
)
|
|
140
240
|
)
|
|
141
241
|
return false;
|
|
142
242
|
if (kind === "action" && input.capabilityApproval !== "none" && input.capabilityApproval !== "rememberable") return false;
|
|
@@ -126,8 +126,9 @@ const fileName = (name: string) => {
|
|
|
126
126
|
};
|
|
127
127
|
|
|
128
128
|
function htmlForm(input: RenderHtmlToPdfInput, config: GotenbergConfig): FormData {
|
|
129
|
-
const total =
|
|
130
|
-
|
|
129
|
+
const total =
|
|
130
|
+
[input.html, input.headerHtml ?? "", input.footerHtml ?? ""].reduce((sum, value) => sum + byteLength(value), 0) +
|
|
131
|
+
(input.assets ?? []).reduce((sum, asset) => sum + asset.data.size, 0);
|
|
131
132
|
if (total > config.maxHtmlBytes) throw new GotenbergRenderError("html_too_large", "HTML and assets exceed the configured input budget.");
|
|
132
133
|
const form = new FormData();
|
|
133
134
|
form.append("files", new Blob([input.html], { type: "text/html" }), "index.html");
|
|
@@ -136,7 +137,8 @@ function htmlForm(input: RenderHtmlToPdfInput, config: GotenbergConfig): FormDat
|
|
|
136
137
|
const names = new Set(["index.html", "header.html", "footer.html", "factur-x.xml"]);
|
|
137
138
|
for (const asset of input.assets ?? []) {
|
|
138
139
|
const name = fileName(asset.name);
|
|
139
|
-
if (names.has(name.toLowerCase()))
|
|
140
|
+
if (names.has(name.toLowerCase()))
|
|
141
|
+
throw new GotenbergRenderError("bad_input", "Asset filenames must be unique and cannot replace HTML documents.");
|
|
140
142
|
names.add(name.toLowerCase());
|
|
141
143
|
form.append("files", asset.data, name);
|
|
142
144
|
}
|
|
@@ -151,7 +153,8 @@ function htmlForm(input: RenderHtmlToPdfInput, config: GotenbergConfig): FormDat
|
|
|
151
153
|
form.append("landscape", String(input.page.landscape ?? false));
|
|
152
154
|
for (const side of ["top", "right", "bottom", "left"] as const) {
|
|
153
155
|
const value = input.page.margin?.[side] ?? 15;
|
|
154
|
-
if (!Number.isFinite(value) || value < 0)
|
|
156
|
+
if (!Number.isFinite(value) || value < 0)
|
|
157
|
+
throw new GotenbergRenderError("bad_input", "Page margins must be nonnegative millimeters.");
|
|
155
158
|
form.append(`margin${side[0]!.toUpperCase()}${side.slice(1)}`, String(value / 25.4));
|
|
156
159
|
}
|
|
157
160
|
}
|
|
@@ -159,14 +162,24 @@ function htmlForm(input: RenderHtmlToPdfInput, config: GotenbergConfig): FormDat
|
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
/** Bounded transport shared by HTML conversion and attachment embedding. */
|
|
162
|
-
async function requestPdf(
|
|
165
|
+
async function requestPdf(
|
|
166
|
+
route: string,
|
|
167
|
+
form: FormData,
|
|
168
|
+
config: GotenbergConfig,
|
|
169
|
+
options: RenderHtmlToPdfOptions,
|
|
170
|
+
): Promise<RenderHtmlToPdfResult> {
|
|
163
171
|
const headers = new Headers();
|
|
164
172
|
const auth = basicAuthHeader(config);
|
|
165
173
|
if (auth) headers.set("Authorization", auth);
|
|
166
174
|
const signal = options.signal ? AbortSignal.any([options.signal, abortSignal(config.timeoutMs)]) : abortSignal(config.timeoutMs);
|
|
167
175
|
let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
|
|
168
176
|
try {
|
|
169
|
-
const response = await (options.fetch ?? fetch)(`${normalizeBaseUrl(config.url)}${route}`, {
|
|
177
|
+
const response = await (options.fetch ?? fetch)(`${normalizeBaseUrl(config.url)}${route}`, {
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers,
|
|
180
|
+
body: form,
|
|
181
|
+
signal,
|
|
182
|
+
});
|
|
170
183
|
if (!response.ok) {
|
|
171
184
|
await response.body?.cancel();
|
|
172
185
|
throw new GotenbergRenderError("bad_response", `Gotenberg returned HTTP ${response.status}.`, response.status);
|
|
@@ -190,20 +203,36 @@ async function requestPdf(route: string, form: FormData, config: GotenbergConfig
|
|
|
190
203
|
}
|
|
191
204
|
const pdf = new Uint8Array(size);
|
|
192
205
|
let offset = 0;
|
|
193
|
-
for (const chunk of chunks) {
|
|
206
|
+
for (const chunk of chunks) {
|
|
207
|
+
pdf.set(chunk, offset);
|
|
208
|
+
offset += chunk.byteLength;
|
|
209
|
+
}
|
|
194
210
|
return { pdf, contentType };
|
|
195
211
|
} catch (error) {
|
|
196
212
|
await reader?.cancel().catch(() => {});
|
|
197
213
|
throw sanitizeFetchError(error);
|
|
198
|
-
} finally {
|
|
214
|
+
} finally {
|
|
215
|
+
reader?.releaseLock();
|
|
216
|
+
}
|
|
199
217
|
}
|
|
200
218
|
|
|
201
|
-
export const renderHtmlToPdfWithConfig = async (
|
|
202
|
-
|
|
219
|
+
export const renderHtmlToPdfWithConfig = async (
|
|
220
|
+
input: RenderHtmlToPdfInput,
|
|
221
|
+
config: GotenbergConfig,
|
|
222
|
+
options: RenderHtmlToPdfOptions = {},
|
|
223
|
+
): Promise<RenderHtmlToPdfResult> => requestPdf("/forms/chromium/convert/html", htmlForm(input, config), config, options);
|
|
203
224
|
|
|
204
|
-
export const renderFacturXHtmlToPdfWithConfig = async (
|
|
225
|
+
export const renderFacturXHtmlToPdfWithConfig = async (
|
|
226
|
+
input: RenderFacturXHtmlToPdfInput,
|
|
227
|
+
config: GotenbergConfig,
|
|
228
|
+
options: RenderHtmlToPdfOptions = {},
|
|
229
|
+
): Promise<RenderHtmlToPdfResult> => {
|
|
205
230
|
const xmlBytes = byteLength(input.xml);
|
|
206
|
-
if (!xmlBytes || xmlBytes > config.maxHtmlBytes)
|
|
231
|
+
if (!xmlBytes || xmlBytes > config.maxHtmlBytes)
|
|
232
|
+
throw new GotenbergRenderError(
|
|
233
|
+
xmlBytes ? "html_too_large" : "bad_input",
|
|
234
|
+
"Invoice XML is empty or exceeds the configured input budget.",
|
|
235
|
+
);
|
|
207
236
|
const form = htmlForm(input, { ...config, maxHtmlBytes: config.maxHtmlBytes - xmlBytes });
|
|
208
237
|
form.append("facturxXml", new Blob([input.xml], { type: "application/xml" }), "factur-x.xml");
|
|
209
238
|
form.append("facturxConformanceLevel", input.conformanceLevel ?? "EN 16931");
|
|
@@ -213,7 +242,11 @@ export const renderFacturXHtmlToPdfWithConfig = async (input: RenderFacturXHtmlT
|
|
|
213
242
|
return requestPdf("/forms/chromium/convert/html", form, config, options);
|
|
214
243
|
};
|
|
215
244
|
|
|
216
|
-
export const attachPdfFilesWithConfig = async (
|
|
245
|
+
export const attachPdfFilesWithConfig = async (
|
|
246
|
+
input: AttachPdfFilesInput,
|
|
247
|
+
config: GotenbergConfig,
|
|
248
|
+
options: RenderHtmlToPdfOptions = {},
|
|
249
|
+
): Promise<RenderHtmlToPdfResult> => {
|
|
217
250
|
if (!input.attachments.length) throw new GotenbergRenderError("bad_input", "At least one attachment is required.");
|
|
218
251
|
if (input.document.size + input.attachments.reduce((sum, file) => sum + file.data.size, 0) > config.maxPdfBytes)
|
|
219
252
|
throw new GotenbergRenderError("pdf_too_large", "PDF and attachments exceed the configured input budget.");
|
|
@@ -226,7 +259,10 @@ export const attachPdfFilesWithConfig = async (input: AttachPdfFilesInput, confi
|
|
|
226
259
|
if (names.has(name.toLowerCase())) throw new GotenbergRenderError("bad_input", "Attachment filenames must be unique.");
|
|
227
260
|
names.add(name.toLowerCase());
|
|
228
261
|
form.append("embeds", attachment.data, name);
|
|
229
|
-
metadata[name] = {
|
|
262
|
+
metadata[name] = {
|
|
263
|
+
mimeType: attachment.data.type || "application/octet-stream",
|
|
264
|
+
relationship: attachment.relationship ?? "Unspecified",
|
|
265
|
+
};
|
|
230
266
|
}
|
|
231
267
|
form.append("embedsMetadata", JSON.stringify(metadata));
|
|
232
268
|
return requestPdf("/forms/pdfengines/embed", form, config, options);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type {
|
|
2
|
-
GotenbergConfig,
|
|
3
2
|
AttachPdfFilesInput,
|
|
3
|
+
GotenbergConfig,
|
|
4
4
|
GotenbergRenderErrorCode,
|
|
5
5
|
MergePdfsInput,
|
|
6
6
|
RenderFacturXHtmlToPdfInput,
|
|
@@ -9,10 +9,10 @@ export type {
|
|
|
9
9
|
RenderHtmlToPdfResult,
|
|
10
10
|
} from "./gotenberg";
|
|
11
11
|
export {
|
|
12
|
-
GotenbergRenderError,
|
|
13
|
-
getGotenbergConfig,
|
|
14
12
|
attachPdfFiles,
|
|
15
13
|
attachPdfFilesWithConfig,
|
|
14
|
+
GotenbergRenderError,
|
|
15
|
+
getGotenbergConfig,
|
|
16
16
|
mergePdfs,
|
|
17
17
|
mergePdfsWithConfig,
|
|
18
18
|
renderFacturXHtmlToPdf,
|
|
@@ -21,14 +21,6 @@ export {
|
|
|
21
21
|
renderHtmlToPdfWithConfig,
|
|
22
22
|
testGotenberg,
|
|
23
23
|
} from "./gotenberg";
|
|
24
|
-
export type {
|
|
25
|
-
RenderTemplatePdfPreviewInput,
|
|
26
|
-
RenderTemplatePdfPreviewOptions,
|
|
27
|
-
TemplatePdfPreviewError,
|
|
28
|
-
TemplatePdfPreviewPhase,
|
|
29
|
-
TemplatePdfPreviewResult,
|
|
30
|
-
} from "./template-preview";
|
|
31
|
-
export { renderTemplatePdfPreview } from "./template-preview";
|
|
32
24
|
export type {
|
|
33
25
|
MarkdownPdfErrorCode,
|
|
34
26
|
MarkdownPdfTemplateId,
|
|
@@ -44,3 +36,11 @@ export {
|
|
|
44
36
|
renderMarkdownToPdf,
|
|
45
37
|
renderMarkdownToPdfWithConfig,
|
|
46
38
|
} from "./markdown";
|
|
39
|
+
export type {
|
|
40
|
+
RenderTemplatePdfPreviewInput,
|
|
41
|
+
RenderTemplatePdfPreviewOptions,
|
|
42
|
+
TemplatePdfPreviewError,
|
|
43
|
+
TemplatePdfPreviewPhase,
|
|
44
|
+
TemplatePdfPreviewResult,
|
|
45
|
+
} from "./template-preview";
|
|
46
|
+
export { renderTemplatePdfPreview } from "./template-preview";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { request, type RequestOptions } from "node:https";
|
|
2
1
|
import type { ClientRequest, IncomingMessage } from "node:http";
|
|
2
|
+
import { type RequestOptions, request } from "node:https";
|
|
3
3
|
import { resolvePublicNetworkAddresses } from "./network-security";
|
|
4
4
|
|
|
5
5
|
export type PublicHttpInput = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SQL, sql } from "bun";
|
|
2
|
-
import {
|
|
2
|
+
import { type RailAdminInput, RailAdminInputSchema, RailAdminSchema, type RailAdminState } from "../contracts/rail-admin";
|
|
3
3
|
import { RailShortcutSchema } from "../contracts/rail-preferences";
|
|
4
4
|
import { hasRole, type User } from "../contracts/shared";
|
|
5
5
|
import { buildAccessPrincipalCondition, createAccess } from "../server/services/access";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { requestCacheRedis } from "./request-cache-redis";
|
|
2
1
|
import { type RailSnapshot, RailSnapshotSchema } from "../contracts/rail-preferences";
|
|
3
2
|
import type { User } from "../contracts/shared";
|
|
4
3
|
import { railPreferences } from "./rail-preferences";
|
|
5
4
|
import { railShortcuts } from "./rail-shortcuts";
|
|
5
|
+
import { requestCacheRedis } from "./request-cache-redis";
|
|
6
6
|
|
|
7
7
|
// Private request metadata: never serialized into the public User contract.
|
|
8
8
|
const versions = new WeakMap<User, string>();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RedisClient } from "bun";
|
|
2
|
+
import { env } from "../config/env";
|
|
2
3
|
|
|
3
4
|
// Cache commands must not queue across a disconnect: delayed fills/invalidations
|
|
4
5
|
// are not useful, and reads must be able to fall back to Postgres. Keep this
|
|
@@ -9,7 +10,7 @@ let reconnecting: Promise<void> | undefined;
|
|
|
9
10
|
|
|
10
11
|
export const requestCacheRedis = async (): Promise<RedisClient> => {
|
|
11
12
|
if (!client) {
|
|
12
|
-
client = new RedisClient(
|
|
13
|
+
client = new RedisClient(env.REDIS_URL, { enableOfflineQueue: false });
|
|
13
14
|
// The first operation waits for normal connection establishment (Bun's
|
|
14
15
|
// connection timeout applies). Later disconnected operations fail promptly.
|
|
15
16
|
initialConnection = client.connect().catch(() => {});
|
|
@@ -6,8 +6,8 @@ import { resolveProviderProfile } from "../accounts/base-user";
|
|
|
6
6
|
import { managedGroupIdsSubquery } from "../accounts/group-sql";
|
|
7
7
|
import { buildIpaUserData, emptyIpaUserData, userIpaDataColumns, userIpaDataJoin } from "../accounts/ipa-data";
|
|
8
8
|
import { resolveAccountExpires } from "../accounts/model";
|
|
9
|
-
import { setRailCacheVersion } from "../rail-snapshot";
|
|
10
9
|
import { toPgTextArray } from "../postgres";
|
|
10
|
+
import { setRailCacheVersion } from "../rail-snapshot";
|
|
11
11
|
|
|
12
12
|
type DbRow = Record<string, unknown>;
|
|
13
13
|
|
|
@@ -30,7 +30,11 @@ const redactSecretValue = (entry: SettingEntry): SettingEntry => {
|
|
|
30
30
|
|
|
31
31
|
export const settingsService = {
|
|
32
32
|
entry: {
|
|
33
|
-
list: async (config?: {
|
|
33
|
+
list: async (config?: {
|
|
34
|
+
pagination?: PageParams;
|
|
35
|
+
filter?: { query?: string; group?: string };
|
|
36
|
+
locale?: string;
|
|
37
|
+
}): Promise<Paginated<SettingEntry>> => {
|
|
34
38
|
const entries = await settingsPrimitives.getAll(config?.locale);
|
|
35
39
|
const query = config?.filter?.query?.trim().toLowerCase();
|
|
36
40
|
const group = config?.filter?.group?.trim().toLowerCase();
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { env } from "../../config/env";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Platform-owned settings, declared in the `defineApp({ settings: ... })` shape.
|
|
3
5
|
*
|
|
@@ -14,11 +16,6 @@
|
|
|
14
16
|
* - keep `description`, `label`, `placeholder`, `default`, `kind` complete
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
|
-
const envString = (key: string): string | undefined => {
|
|
18
|
-
const value = process.env[key]?.trim();
|
|
19
|
-
return value && value.length > 0 ? value : undefined;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
19
|
const IPA_MATCH_MODE_OPTIONS = [
|
|
23
20
|
{ value: "ignore", label: "Ignore local match" },
|
|
24
21
|
{ value: "migrate", label: "Migrate matching local account" },
|
|
@@ -124,8 +121,8 @@ export const CORE_SETTINGS = {
|
|
|
124
121
|
description:
|
|
125
122
|
"Public-facing application URL used for links in emails, OAuth redirects, and WebSocket connections (with or without scheme)",
|
|
126
123
|
placeholder: "e.g. https://cloud.example.org",
|
|
127
|
-
envFallback: () =>
|
|
128
|
-
envBootstrap: () =>
|
|
124
|
+
envFallback: () => env.APP_URL,
|
|
125
|
+
envBootstrap: () => env.APP_URL,
|
|
129
126
|
},
|
|
130
127
|
"app.home_path": {
|
|
131
128
|
kind: "string",
|
|
@@ -294,7 +291,8 @@ export const CORE_SETTINGS = {
|
|
|
294
291
|
integer: true,
|
|
295
292
|
default: 30,
|
|
296
293
|
min: 0,
|
|
297
|
-
description:
|
|
294
|
+
description:
|
|
295
|
+
"Maximum running time per AI turn. Zero or an empty field disables this limit. Existing turns keep their original budget; request and tool timeouts still apply.",
|
|
298
296
|
},
|
|
299
297
|
"ai.max_tool_result_chars": {
|
|
300
298
|
kind: "number",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
import { redis, sql } from "bun";
|
|
13
13
|
import { decryptValue, encryptValue, getAppSecret } from "./crypto";
|
|
14
|
-
import { SETTINGS, SETTINGS_MAP,
|
|
15
|
-
import { bulkRead, deleteKey,
|
|
14
|
+
import { resolveSettingPresentation, SETTINGS, SETTINGS_MAP, type SettingDef, validateSettingValue } from "./defaults";
|
|
15
|
+
import { bulkRead, deleteKey, readKey, writeKey } from "./store";
|
|
16
16
|
|
|
17
17
|
type SqlClient = typeof sql;
|
|
18
18
|
|
|
@@ -175,8 +175,8 @@ export async function remove(key: string, db?: SqlClient): Promise<void> {
|
|
|
175
175
|
|
|
176
176
|
import type { SettingEntry } from "../../contracts/shared";
|
|
177
177
|
|
|
178
|
-
export { invalidateSettingsCache } from "./store";
|
|
179
178
|
export type { SettingEntry } from "../../contracts/shared";
|
|
179
|
+
export { invalidateSettingsCache } from "./store";
|
|
180
180
|
|
|
181
181
|
export async function getAll(locale?: string): Promise<SettingEntry[]> {
|
|
182
182
|
// Determine which keys have a custom row in Postgres (vs. falling back to
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
* snapshot exposed via `c.get("settings")` (built by snapshot.ts middleware).
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { sql } from "bun";
|
|
16
17
|
import { HTTPException } from "hono/http-exception";
|
|
17
18
|
import { hasRole, type User } from "../../contracts/shared";
|
|
18
|
-
import { sql } from "bun";
|
|
19
|
-
import { requestCacheRedis } from "../request-cache-redis";
|
|
20
|
-
import { toPgTextArray } from "../postgres";
|
|
21
19
|
import { claimCacheFill, completeCacheFill, MISSING_SETTING } from "../cache-fill";
|
|
20
|
+
import { toPgTextArray } from "../postgres";
|
|
21
|
+
import { requestCacheRedis } from "../request-cache-redis";
|
|
22
22
|
import { decryptValue, encryptValue } from "./crypto";
|
|
23
23
|
import { SETTINGS, SETTINGS_MAP, type SettingDef, validateSettingValue } from "./defaults";
|
|
24
24
|
|
package/src/services/webauthn.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { err, fail, ok, type Result } from "@k2b/stdlib";
|
|
1
2
|
import {
|
|
2
3
|
type AuthenticationResponseJSON,
|
|
3
4
|
type AuthenticatorTransportFuture,
|
|
@@ -10,7 +11,6 @@ import {
|
|
|
10
11
|
verifyRegistrationResponse,
|
|
11
12
|
type WebAuthnCredential,
|
|
12
13
|
} from "@simplewebauthn/server";
|
|
13
|
-
import { err, fail, ok, type Result } from "@k2b/stdlib";
|
|
14
14
|
import { redis, sql } from "bun";
|
|
15
15
|
import type { User, WebAuthnPasskey } from "../contracts/shared";
|
|
16
16
|
import { accounts } from "./accounts";
|
package/src/shared/index.ts
CHANGED
|
@@ -11,8 +11,11 @@ export {
|
|
|
11
11
|
} from "./account-action-notice";
|
|
12
12
|
export * from "./account-display";
|
|
13
13
|
export * from "./account-session";
|
|
14
|
+
export * from "./ai-costs";
|
|
14
15
|
export type { AiPromptContextInput, AiToolPromptHint } from "./ai-platform-prompt";
|
|
15
16
|
export { AI_PLATFORM_PROMPT_TEMPLATE, aiPromptContext, renderAiPlatformPrompt } from "./ai-platform-prompt";
|
|
17
|
+
export type { AiChatQuotaSnapshot } from "./ai-quotas";
|
|
18
|
+
export * from "./ai-quotas";
|
|
16
19
|
export {
|
|
17
20
|
AI_USAGE_RANGES,
|
|
18
21
|
AI_USAGE_REASONS,
|
|
@@ -52,9 +55,3 @@ export {
|
|
|
52
55
|
} from "./template-rendering";
|
|
53
56
|
export * from "./theme";
|
|
54
57
|
export * from "./time";
|
|
55
|
-
|
|
56
|
-
export * from "./ai-quotas";
|
|
57
|
-
|
|
58
|
-
export type { AiChatQuotaSnapshot } from "./ai-quotas";
|
|
59
|
-
|
|
60
|
-
export * from "./ai-costs";
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Supported types: note, info, success, warning, danger
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import type { MarkedExtension, Tokens } from "marked";
|
|
17
16
|
import { NOTICE_CARD_CLASSES, NOTICE_CARD_ICONS, type NoticeTone } from "@k2b/ui";
|
|
17
|
+
import type { MarkedExtension, Tokens } from "marked";
|
|
18
18
|
import { escapeHtml } from "../shared";
|
|
19
19
|
|
|
20
20
|
type BlockType = "note" | "info" | "success" | "warning" | "danger";
|
package/src/ssr/AdminSidebar.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import WorkspaceNavigation from "./WorkspaceNavigation.island";
|
|
2
1
|
import { AppWorkspace, useLocale } from "@k2b/ui";
|
|
3
2
|
import { activeAdminHref } from "./admin-active-link";
|
|
4
3
|
import { type AdminLink, buildAdminGroups } from "./admin-navigation";
|
|
5
|
-
import type { RuntimeContext } from "./runtime";
|
|
6
4
|
import { platformMessages } from "./platform-messages";
|
|
5
|
+
import type { RuntimeContext } from "./runtime";
|
|
6
|
+
import WorkspaceNavigation from "./WorkspaceNavigation.island";
|
|
7
7
|
|
|
8
8
|
const AdminNavigation = (props: { currentPath: string; groups: ReturnType<typeof buildAdminGroups> }) => {
|
|
9
9
|
const activeHref = activeAdminHref(
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type AppLaunchpadApp, type AppLaunchpadContext, type AppLaunchpadLegalLink, AppLaunchpadPanel } from "./AppLaunchpadPanel";
|
|
2
2
|
import { openCloudMobileMenu } from "./MobileNavigation";
|
|
3
|
+
|
|
3
4
|
export type { AppLaunchpadApp, AppLaunchpadLegalLink } from "./AppLaunchpadPanel";
|
|
5
|
+
|
|
4
6
|
import { dialogCore, IconButton, Tooltip, useLocale } from "@k2b/ui";
|
|
5
7
|
import { createEffect } from "solid-js";
|
|
6
8
|
import { platformMessages } from "./platform-messages";
|
|
@@ -117,7 +119,15 @@ export function AppLaunchpad(props: AppLaunchpadContext & { variant?: "provider"
|
|
|
117
119
|
return <AppLaunchpadProvider apps={props.apps} legalLinks={props.legalLinks} profile={props.profile} />;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
return
|
|
122
|
+
return (
|
|
123
|
+
<AppLaunchpadButton
|
|
124
|
+
apps={props.apps}
|
|
125
|
+
legalLinks={props.legalLinks}
|
|
126
|
+
profile={props.profile}
|
|
127
|
+
variant={props.variant}
|
|
128
|
+
label={props.label}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
export default AppLaunchpad;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { documentNavigate } from "@k2b/ssr/nav";
|
|
2
|
+
import { IconButton, ScrollArea, TextInput, useLocale } from "@k2b/ui";
|
|
3
|
+
import { createSignal, For, type JSX, Show } from "solid-js";
|
|
4
|
+
import { Dynamic } from "solid-js/web";
|
|
1
5
|
import type { CloudTheme } from "../shared/theme";
|
|
2
6
|
import { MobileProfileActions } from "./MobileProfileActions";
|
|
3
|
-
import { Dynamic } from "solid-js/web";
|
|
4
7
|
import { platformMessages } from "./platform-messages";
|
|
5
|
-
import { IconButton, ScrollArea, TextInput, useLocale } from "@k2b/ui";
|
|
6
|
-
import { createSignal, For, Show, type JSX } from "solid-js";
|
|
7
|
-
import { documentNavigate } from "@k2b/ssr/nav";
|
|
8
8
|
import { openRailEditor } from "./RailEditor";
|
|
9
9
|
import { readRailContext } from "./rail-context";
|
|
10
10
|
import { railMessages } from "./rail-messages";
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { type DialogRender, dialogCore, toast, useLocale } from "@k2b/ui";
|
|
2
1
|
import { query } from "@k2b/stdlib/solid";
|
|
3
|
-
import {
|
|
4
|
-
import { COMMANDS_CHANGED, collectContextAwareCommands } from "../browser/command-bridge";
|
|
5
|
-
import { contextCommandsWithShortcuts } from "../browser/command-shortcuts";
|
|
6
|
-
import { openCommand, runContextAwareCommand } from "../browser/commands";
|
|
7
|
-
import { loadSearchCommands, type PaletteCommand } from "../browser/search-commands";
|
|
2
|
+
import { type DialogRender, dialogCore, toast, useLocale } from "@k2b/ui";
|
|
8
3
|
import { createEffect, createSignal, For, onCleanup, onMount } from "solid-js";
|
|
9
4
|
import type { SearchItem } from "../api/search/schemas";
|
|
10
5
|
import CloudResourceSearch from "../browser/CloudResourceSearch";
|
|
6
|
+
import { COMMANDS_CHANGED, collectContextAwareCommands } from "../browser/command-bridge";
|
|
7
|
+
import { contextCommandsWithShortcuts } from "../browser/command-shortcuts";
|
|
8
|
+
import { openCommand, runContextAwareCommand } from "../browser/commands";
|
|
11
9
|
import type { NavigationSearchItem } from "../browser/navigation-search";
|
|
12
10
|
import { resourceSearchDialogOptions } from "../browser/resource-search-dialog";
|
|
13
11
|
import { resourceSearchMessages } from "../browser/resource-search-messages";
|
|
14
12
|
import { type GlobalSearchOptions, requestSearchNavigation } from "../browser/search-bridge";
|
|
13
|
+
import { loadSearchCommands, type PaletteCommand } from "../browser/search-commands";
|
|
15
14
|
import { attachSpotlightPosition } from "../browser/spotlight-position";
|
|
15
|
+
import { resolveCommand } from "../capabilities/client";
|
|
16
16
|
|
|
17
17
|
type GlobalSearchDialogProps = {
|
|
18
18
|
request?: GlobalSearchOptions;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { registerContextAwareCommand, runContextAwareCommand } from "../browser/commands";
|
|
2
|
-
import { attachCommandShortcuts } from "../browser/command-shortcuts";
|
|
3
1
|
import { IconButton, Tooltip, useLocale } from "@k2b/ui";
|
|
4
2
|
import { createEffect, onCleanup, onMount } from "solid-js";
|
|
3
|
+
import { attachCommandShortcuts } from "../browser/command-shortcuts";
|
|
4
|
+
import { registerContextAwareCommand, runContextAwareCommand } from "../browser/commands";
|
|
5
5
|
import type { NavigationSearchItem } from "../browser/navigation-search";
|
|
6
6
|
import { openGlobalSearch } from "../browser/search";
|
|
7
7
|
import { registerGlobalSearchHost } from "../browser/search-bridge";
|
package/src/ssr/Layout.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { getLayoutHelp } from "./help";
|
|
2
|
-
import { navigationSearchItems } from "../browser/navigation-search";
|
|
3
1
|
import type { DateContext } from "@k2b/stdlib";
|
|
4
2
|
import { AppWorkspace, appWorkspaceLayoutStyle, LocaleProvider, NoticeCard } from "@k2b/ui";
|
|
5
3
|
import type { JSX } from "solid-js/jsx-runtime";
|
|
6
4
|
import { readAppWorkspaceLayoutCookie, resolveAppWorkspaceLayoutForSidebar } from "../_internal/app-workspace-state";
|
|
5
|
+
import { navigationSearchItems } from "../browser/navigation-search";
|
|
7
6
|
import { resolveNavMatch } from "../contracts/app"; // ==========================
|
|
8
7
|
import { defaultRailPreferences } from "../contracts/rail-preferences";
|
|
9
8
|
import { hasRole, type User } from "../contracts/shared";
|
|
@@ -18,6 +17,7 @@ import { visibleNavigationApps } from "./app-navigation";
|
|
|
18
17
|
import BrowserPushRegistration from "./BrowserPushRegistration.island";
|
|
19
18
|
import GlobalAnnouncements from "./GlobalAnnouncements.island";
|
|
20
19
|
import type { GlobalSearchHelpApp } from "./GlobalSearchHelpDialog";
|
|
20
|
+
import { getLayoutHelp } from "./help";
|
|
21
21
|
import LayoutFooter from "./LayoutFooter";
|
|
22
22
|
import LayoutHeader from "./LayoutHeader";
|
|
23
23
|
import LayoutRail, { type LayoutAppLink } from "./LayoutRail";
|