@noodleseed/one 0.154.0 → 0.156.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/README.md +5 -1
- package/dist/assistant-embed-django-tests.d.ts +3 -0
- package/dist/assistant-embed-django-tests.d.ts.map +1 -0
- package/dist/assistant-embed-django-tests.js +140 -0
- package/dist/assistant-embed-django-tests.js.map +1 -0
- package/dist/assistant-embed-django.d.ts +4 -0
- package/dist/assistant-embed-django.d.ts.map +1 -0
- package/dist/assistant-embed-django.js +259 -0
- package/dist/assistant-embed-django.js.map +1 -0
- package/dist/assistant-embed-installation.d.ts +29 -0
- package/dist/assistant-embed-installation.d.ts.map +1 -0
- package/dist/assistant-embed-installation.js +124 -0
- package/dist/assistant-embed-installation.js.map +1 -0
- package/dist/assistant-embed-profile.d.ts +8 -0
- package/dist/assistant-embed-profile.d.ts.map +1 -0
- package/dist/assistant-embed-profile.js +150 -0
- package/dist/assistant-embed-profile.js.map +1 -0
- package/dist/assistant-embed-scaffold-template.d.ts +3 -2
- package/dist/assistant-embed-scaffold-template.d.ts.map +1 -1
- package/dist/assistant-embed-scaffold-template.js +39 -84
- package/dist/assistant-embed-scaffold-template.js.map +1 -1
- package/dist/assistant-embed-tests.d.ts +5 -0
- package/dist/assistant-embed-tests.d.ts.map +1 -0
- package/dist/assistant-embed-tests.js +106 -0
- package/dist/assistant-embed-tests.js.map +1 -0
- package/dist/assistant-embed-vue.d.ts +3 -0
- package/dist/assistant-embed-vue.d.ts.map +1 -0
- package/dist/assistant-embed-vue.js +77 -0
- package/dist/assistant-embed-vue.js.map +1 -0
- package/dist/commands/assistant-embed-ops.d.ts.map +1 -1
- package/dist/commands/assistant-embed-ops.js +112 -63
- package/dist/commands/assistant-embed-ops.js.map +1 -1
- package/dist/commands/author-loop.d.ts.map +1 -1
- package/dist/commands/author-loop.js +24 -28
- package/dist/commands/author-loop.js.map +1 -1
- package/dist/commands/deploy-first-flow.d.ts.map +1 -1
- package/dist/commands/deploy-first-flow.js +26 -15
- package/dist/commands/deploy-first-flow.js.map +1 -1
- package/dist/commands/deploy-preflight.d.ts +5 -0
- package/dist/commands/deploy-preflight.d.ts.map +1 -1
- package/dist/commands/deploy-preflight.js +44 -5
- package/dist/commands/deploy-preflight.js.map +1 -1
- package/dist/commands/local-readiness.d.ts +9 -1
- package/dist/commands/local-readiness.d.ts.map +1 -1
- package/dist/commands/local-readiness.js +74 -4
- package/dist/commands/local-readiness.js.map +1 -1
- package/dist/commands/project-setup.d.ts.map +1 -1
- package/dist/commands/project-setup.js +10 -15
- package/dist/commands/project-setup.js.map +1 -1
- package/dist/control-plane-request.d.ts +4 -0
- package/dist/control-plane-request.d.ts.map +1 -1
- package/dist/control-plane-request.js +40 -20
- package/dist/control-plane-request.js.map +1 -1
- package/dist/project-scaffold-model.d.ts +14 -0
- package/dist/project-scaffold-model.d.ts.map +1 -0
- package/dist/project-scaffold-model.js +36 -0
- package/dist/project-scaffold-model.js.map +1 -0
- package/dist/project-scaffold-templates.d.ts.map +1 -1
- package/dist/project-scaffold-templates.js +73 -39
- package/dist/project-scaffold-templates.js.map +1 -1
- package/dist/project-scaffold-test-template.d.ts +4 -0
- package/dist/project-scaffold-test-template.d.ts.map +1 -0
- package/dist/project-scaffold-test-template.js +89 -0
- package/dist/project-scaffold-test-template.js.map +1 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +1 -2
- package/dist/project.js.map +1 -1
- package/dist/widget-focused-template.d.ts +1 -1
- package/dist/widget-focused-template.d.ts.map +1 -1
- package/dist/widget-focused-template.js +34 -12
- package/dist/widget-focused-template.js.map +1 -1
- package/dist/widget-scaffold-template.d.ts +1 -1
- package/dist/widget-scaffold-template.d.ts.map +1 -1
- package/dist/widget-scaffold-template.js +75 -43
- package/dist/widget-scaffold-template.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +18 -4
- package/node_modules/@noodle-borg/agent-kit/dist/skill-app-playbook-ref.js +2 -2
- package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +9 -12
- package/node_modules/@noodle-borg/agent-kit/dist/skill-connect-refs.js +2 -2
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-operations-ref.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +34 -26
- package/node_modules/@noodle-borg/agent-kit/dist/skill-server-playbook-ref.js +2 -2
- package/node_modules/@noodle-borg/agent-kit/dist/skill-verification-ref.js +2 -1
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-suggestions.js +5 -3
- package/node_modules/@noodle-borg/assistant-gateway/dist/continuity-bounds.d.ts +36 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/continuity-bounds.js +52 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/continuity-store.d.ts +115 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/continuity-store.js +51 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-continuity-store.d.ts +26 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/in-memory-continuity-store.js +80 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.d.ts +1 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-request.js +2 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-response-values.js +11 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-responses.js +1 -10
- package/node_modules/@noodle-borg/assistant-gateway/dist/model-stream.js +1 -10
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +3 -0
- package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +3 -0
- package/node_modules/@noodle-borg/assistant-gateway/package.json +1 -1
- package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +41 -0
- package/node_modules/@noodle-borg/authoring/dist/assistant.js +4 -0
- package/node_modules/@noodle-borg/authoring/dist/recording.d.ts +1 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-assistant.js +10 -4
- package/node_modules/@noodle-borg/compiler/dist/manifest/branding-schema.js +86 -0
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +44 -25
- package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +24 -76
- package/node_modules/@noodle-borg/compiler/package.json +0 -1
- package/node_modules/@noodle-borg/deploy-client/dist/deploy-payload-size.js +6 -2
- package/node_modules/@noodle-borg/service/dist/registry-helpers.d.ts +1 -1
- package/node_modules/@noodle-borg/service/dist/routes/deploy-preflight.js +193 -96
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +3 -0
- package/node_modules/@noodle-borg/wire-contracts/dist/index.js +3 -0
- package/node_modules/@noodleseed/assistant/README.md +21 -23
- package/node_modules/@noodleseed/assistant/package.json +1 -1
- package/package.json +5 -4
- package/dist/saas-scaffold-template.d.ts +0 -4
- package/dist/saas-scaffold-template.d.ts.map +0 -1
- package/dist/saas-scaffold-template.js +0 -56
- package/dist/saas-scaffold-template.js.map +0 -1
- package/node_modules/tailwindcss/LICENSE +0 -21
- package/node_modules/tailwindcss/README.md +0 -36
- package/node_modules/tailwindcss/dist/chunk-5JIJA4QV.mjs +0 -1
- package/node_modules/tailwindcss/dist/chunk-HMCCH6MG.mjs +0 -1
- package/node_modules/tailwindcss/dist/chunk-X4GG3EDV.mjs +0 -1
- package/node_modules/tailwindcss/dist/colors-C__qRT83.d.ts +0 -347
- package/node_modules/tailwindcss/dist/colors.d.mts +0 -347
- package/node_modules/tailwindcss/dist/colors.d.ts +0 -5
- package/node_modules/tailwindcss/dist/colors.js +0 -1
- package/node_modules/tailwindcss/dist/colors.mjs +0 -1
- package/node_modules/tailwindcss/dist/default-theme.d.mts +0 -1199
- package/node_modules/tailwindcss/dist/default-theme.d.ts +0 -1199
- package/node_modules/tailwindcss/dist/default-theme.js +0 -1
- package/node_modules/tailwindcss/dist/default-theme.mjs +0 -1
- package/node_modules/tailwindcss/dist/flatten-color-palette.d.mts +0 -6
- package/node_modules/tailwindcss/dist/flatten-color-palette.d.ts +0 -6
- package/node_modules/tailwindcss/dist/flatten-color-palette.js +0 -1
- package/node_modules/tailwindcss/dist/flatten-color-palette.mjs +0 -1
- package/node_modules/tailwindcss/dist/lib.d.mts +0 -379
- package/node_modules/tailwindcss/dist/lib.d.ts +0 -3
- package/node_modules/tailwindcss/dist/lib.js +0 -38
- package/node_modules/tailwindcss/dist/lib.mjs +0 -38
- package/node_modules/tailwindcss/dist/plugin.d.mts +0 -11
- package/node_modules/tailwindcss/dist/plugin.d.ts +0 -134
- package/node_modules/tailwindcss/dist/plugin.js +0 -1
- package/node_modules/tailwindcss/dist/plugin.mjs +0 -1
- package/node_modules/tailwindcss/dist/resolve-config-B4yBzhca.d.ts +0 -29
- package/node_modules/tailwindcss/dist/resolve-config-QUZ9b-Gn.d.mts +0 -190
- package/node_modules/tailwindcss/dist/types-DWdTiksJ.d.mts +0 -128
- package/node_modules/tailwindcss/index.css +0 -944
- package/node_modules/tailwindcss/package.json +0 -89
- package/node_modules/tailwindcss/preflight.css +0 -393
- package/node_modules/tailwindcss/theme.css +0 -510
- package/node_modules/tailwindcss/utilities.css +0 -1
|
@@ -3,6 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import { MAX_COMPILED_WIDGET_HTML_BYTES, MAX_RAW_WIDGET_HTML_BYTES } from '../widget-limits.js';
|
|
4
4
|
import { agentGuideSchema } from './agent-guide-schema.js';
|
|
5
5
|
import { serverAuthSchema, serverAuthV2Schema } from './auth-schema.js';
|
|
6
|
+
import { assistantOriginOrVariableSchema, httpsUrlSchema, managedVariableExpressionSchema, serverBrandingSchema, } from './branding-schema.js';
|
|
6
7
|
import { NAME_PATTERN } from './naming.js';
|
|
7
8
|
/**
|
|
8
9
|
* Zod schema for Core v1 and v2 manifests (ADRs 0150 and 0157). V1 remains accepted unchanged;
|
|
@@ -133,82 +134,6 @@ const promptSchema = z.object({
|
|
|
133
134
|
arguments: z.array(promptArgumentSchema).optional(),
|
|
134
135
|
fulfilment: fulfilmentSchema,
|
|
135
136
|
});
|
|
136
|
-
const hexColorSchema = z.string().regex(/^#[0-9A-Fa-f]{6}$/, 'must be a 6-digit hex color');
|
|
137
|
-
const httpsUrlSchema = z.url().regex(/^https:\/\//, 'must use https');
|
|
138
|
-
const managedVariableExpressionPattern = /^\$\{env\.[A-Za-z0-9_]+\}$/;
|
|
139
|
-
const managedVariableExpressionSchema = z
|
|
140
|
-
.string()
|
|
141
|
-
.regex(managedVariableExpressionPattern, 'must be an exact managed variable expression');
|
|
142
|
-
// HTTP is allowed only for loopback development origins (mirrors the host-pattern loopback
|
|
143
|
-
// exception used for connector/CSP origins); production embedding origins stay https-only.
|
|
144
|
-
const assistantOriginOrVariablePattern = /^(?:\$\{env\.[A-Za-z0-9_]+\}|https:\/\/[^/?#@\\]+|http:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?)$/;
|
|
145
|
-
function isCanonicalAssistantOrigin(value) {
|
|
146
|
-
try {
|
|
147
|
-
return new URL(value).origin === value;
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const assistantOriginOrVariableSchema = z
|
|
154
|
-
.string()
|
|
155
|
-
.regex(assistantOriginOrVariablePattern, 'must be a canonical bare origin using https (http is allowed only for localhost/127.0.0.1/[::1] loopback development origins)')
|
|
156
|
-
.refine((value) => managedVariableExpressionPattern.test(value) || isCanonicalAssistantOrigin(value), 'must be a canonical bare origin');
|
|
157
|
-
const packagedAssetSchema = z
|
|
158
|
-
.object({
|
|
159
|
-
kind: z.literal('asset'),
|
|
160
|
-
sourcePath: z.string().min(1),
|
|
161
|
-
logicalId: z.string().min(1),
|
|
162
|
-
})
|
|
163
|
-
.strict();
|
|
164
|
-
const brandAssetSchema = z
|
|
165
|
-
.object({
|
|
166
|
-
uri: z.union([httpsUrlSchema, packagedAssetSchema]),
|
|
167
|
-
darkUri: z.union([httpsUrlSchema, packagedAssetSchema]).optional(),
|
|
168
|
-
alt: z.string().trim().min(1),
|
|
169
|
-
})
|
|
170
|
-
.strict();
|
|
171
|
-
const brandThemeTokensSchema = z
|
|
172
|
-
.object({
|
|
173
|
-
surface: hexColorSchema.optional(),
|
|
174
|
-
surfaceRaised: hexColorSchema.optional(),
|
|
175
|
-
surfaceMuted: hexColorSchema.optional(),
|
|
176
|
-
text: hexColorSchema.optional(),
|
|
177
|
-
textMuted: hexColorSchema.optional(),
|
|
178
|
-
accent: hexColorSchema.optional(),
|
|
179
|
-
accentText: hexColorSchema.optional(),
|
|
180
|
-
link: hexColorSchema.optional(),
|
|
181
|
-
border: hexColorSchema.optional(),
|
|
182
|
-
borderStrong: hexColorSchema.optional(),
|
|
183
|
-
focus: hexColorSchema.optional(),
|
|
184
|
-
success: hexColorSchema.optional(),
|
|
185
|
-
warning: hexColorSchema.optional(),
|
|
186
|
-
danger: hexColorSchema.optional(),
|
|
187
|
-
code: hexColorSchema.optional(),
|
|
188
|
-
})
|
|
189
|
-
.strict();
|
|
190
|
-
const serverBrandingSchema = z
|
|
191
|
-
.object({
|
|
192
|
-
name: z.string().trim().min(1).optional(),
|
|
193
|
-
accent: hexColorSchema.optional(),
|
|
194
|
-
surface: hexColorSchema.optional(),
|
|
195
|
-
surfaceDark: hexColorSchema.optional(),
|
|
196
|
-
logo: brandAssetSchema.optional(),
|
|
197
|
-
mark: brandAssetSchema.optional(),
|
|
198
|
-
avatar: brandAssetSchema.optional(),
|
|
199
|
-
theme: z
|
|
200
|
-
.object({
|
|
201
|
-
light: brandThemeTokensSchema.optional(),
|
|
202
|
-
dark: brandThemeTokensSchema.optional(),
|
|
203
|
-
})
|
|
204
|
-
.strict()
|
|
205
|
-
.optional(),
|
|
206
|
-
radius: z.enum(['none', 'sm', 'md', 'lg']).optional(),
|
|
207
|
-
density: z.enum(['compact', 'comfortable']).optional(),
|
|
208
|
-
typography: z.enum(['system', 'serif', 'mono']).optional(),
|
|
209
|
-
colorScheme: z.enum(['auto', 'light', 'dark']).optional(),
|
|
210
|
-
})
|
|
211
|
-
.strict();
|
|
212
137
|
const assistantPresentationToneSchema = z.enum(['neutral', 'success', 'warning', 'danger']);
|
|
213
138
|
const optionalStrictObject = (shape) => z.object(shape).strict().optional();
|
|
214
139
|
const assistantPresentationSchema = z
|
|
@@ -357,6 +282,18 @@ const embeddedAssistantSchema = z
|
|
|
357
282
|
// `assistantUiSchema.shape` — this is not renderer presentation, and a surface that
|
|
358
283
|
// acquired it by a spread would be an accident rather than a decision.
|
|
359
284
|
webmcp: optionalStrictObject({ enabled: z.boolean().optional() }),
|
|
285
|
+
// Anonymous cross-page display continuity (ADR 0223, clauses 11-16), declared per public
|
|
286
|
+
// or mixed surface. These bounds mirror the structural ceilings the gateway clamps to at
|
|
287
|
+
// runtime; the ADR is the single source, and this package cannot import the gateway
|
|
288
|
+
// because the dependency runs the other way. Refusing rather than clamping is deliberate:
|
|
289
|
+
// the gateway narrows an operator's value silently by design, but a developer who asks
|
|
290
|
+
// for more than the ceiling has made a mistake, and silence would hide it until someone
|
|
291
|
+
// measured the live behaviour.
|
|
292
|
+
continuity: optionalStrictObject({
|
|
293
|
+
enabled: z.boolean().optional(),
|
|
294
|
+
windowSeconds: z.number().int().min(0).max(600).optional(),
|
|
295
|
+
maxRestores: z.number().int().min(0).max(10).optional(),
|
|
296
|
+
}),
|
|
360
297
|
})
|
|
361
298
|
.strict())
|
|
362
299
|
.min(1)
|
|
@@ -374,6 +311,17 @@ const embeddedAssistantSchema = z
|
|
|
374
311
|
.strict()
|
|
375
312
|
.superRefine((assistant, ctx) => {
|
|
376
313
|
assistant.surfaces?.forEach((surface, index) => {
|
|
314
|
+
// The authenticated direction already reattaches through ADR 0223 clause 7, authorized by a
|
|
315
|
+
// backend-verified subject rather than by possession of a handle. A `continuity` block here
|
|
316
|
+
// would be a developer believing they configured something nothing reads, so refuse it rather
|
|
317
|
+
// than drop it silently.
|
|
318
|
+
if (surface.mode === 'authenticated' && surface.continuity !== undefined) {
|
|
319
|
+
ctx.addIssue({
|
|
320
|
+
code: 'custom',
|
|
321
|
+
path: ['surfaces', index, 'continuity'],
|
|
322
|
+
message: 'continuity belongs on a public or mixed website surface',
|
|
323
|
+
});
|
|
324
|
+
}
|
|
377
325
|
if (surface.mode === 'authenticated')
|
|
378
326
|
return;
|
|
379
327
|
// A public surface fails closed: an omitted allowlist would otherwise read as "expose
|
|
@@ -9,8 +9,12 @@ function largestWidgetContributors(manifest) {
|
|
|
9
9
|
try {
|
|
10
10
|
const parsed = JSON.parse(manifest);
|
|
11
11
|
const contributors = (parsed.widgets ?? [])
|
|
12
|
-
.
|
|
13
|
-
|
|
12
|
+
.flatMap((widget) => {
|
|
13
|
+
const html = widget.html ?? widget.view?.compiledHtml;
|
|
14
|
+
return typeof widget.name === 'string' && typeof html === 'string'
|
|
15
|
+
? [{ name: widget.name, bytes: Buffer.byteLength(html) }]
|
|
16
|
+
: [];
|
|
17
|
+
})
|
|
14
18
|
.sort((left, right) => right.bytes - left.bytes)
|
|
15
19
|
.slice(0, 3);
|
|
16
20
|
return contributors.length === 0
|
|
@@ -15,7 +15,7 @@ export type ActiveDeployProvenance = {
|
|
|
15
15
|
readonly ownerSubject: string | undefined;
|
|
16
16
|
} | undefined;
|
|
17
17
|
/** Resolve the durable owner binding while keeping legacy creator-only records readable. */
|
|
18
|
-
export declare function deploymentOwnerSubject(record: DeployRecord): string | undefined;
|
|
18
|
+
export declare function deploymentOwnerSubject(record: Pick<DeployRecord, 'ownerSubject' | 'createdBySubject'>): string | undefined;
|
|
19
19
|
export declare function missingCapabilityErrors(required: readonly CapabilityName[], available: readonly CapabilityName[]): readonly DeployError[];
|
|
20
20
|
export declare function missingSecretErrors(bindings: readonly SecretBinding[], secrets: Readonly<Record<string, string>>): DeployError[];
|
|
21
21
|
export declare function missingVariableErrors(bindings: readonly string[], variables: Readonly<Record<string, string>>): DeployError[];
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { cspFaultsInManifest } from '@noodle-borg/compiler';
|
|
2
3
|
import { readDeployBody, sendJson } from '@noodle-borg/transport-http';
|
|
3
|
-
import { deployPreflightRequestSchema, formatWireError } from '@noodle-borg/wire-contracts';
|
|
4
|
+
import { DEPLOY_PREFLIGHT_CHECK_TIMEOUT_MS, deployPreflightRequestSchema, formatWireError, } from '@noodle-borg/wire-contracts';
|
|
4
5
|
import { manifestUsesUserRoot } from '../registry-access.js';
|
|
5
6
|
import { isIdentityAccessMode } from './access-mode.js';
|
|
6
7
|
import { authorizeDeploymentOwnerSelection, authorizeTenantControl, developerGrantRouteAccess, } from './control-plane.js';
|
|
8
|
+
// Per-process resource admission, not a tenant quota or distributed state authority.
|
|
9
|
+
const activePreflights = new WeakSet();
|
|
7
10
|
/**
|
|
8
11
|
* Read-only first-deploy readiness. It intentionally does not verify uploaded asset bytes:
|
|
9
12
|
* the CLI calls this before it requests upload targets or sends any bytes.
|
|
10
13
|
*/
|
|
11
14
|
export async function handleDeployPreflight(req, res, registry, options, maxBody, tenant, gate, controlPlane, audit) {
|
|
15
|
+
const startedAt = Date.now();
|
|
16
|
+
const suppliedId = req.headers['x-request-id'];
|
|
17
|
+
const requestId = typeof suppliedId === 'string' &&
|
|
18
|
+
/^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i.test(suppliedId)
|
|
19
|
+
? suppliedId
|
|
20
|
+
: randomUUID();
|
|
21
|
+
res.setHeader('x-request-id', requestId);
|
|
12
22
|
const identity = await authorizeTenantControl(req, res, gate, controlPlane, tenant.org, developerGrantRouteAccess(options.developerGrantStore, 'deployments:write'));
|
|
13
23
|
if (identity === false)
|
|
14
24
|
return;
|
|
@@ -25,111 +35,198 @@ export async function handleDeployPreflight(req, res, registry, options, maxBody
|
|
|
25
35
|
error: 'app is archived; restore it before deploying',
|
|
26
36
|
});
|
|
27
37
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
parsed = result.data;
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
return sendJson(res, 400, {
|
|
41
|
-
code: 'invalid_deploy_request',
|
|
42
|
-
error: `invalid deploy preflight request: ${error.message}`,
|
|
38
|
+
if (activePreflights.has(registry)) {
|
|
39
|
+
await audit.emit({
|
|
40
|
+
eventType: 'deploy.preflight.rejected',
|
|
41
|
+
...tenant,
|
|
42
|
+
actorSubject: identity.subject,
|
|
43
|
+
decision: 'deny',
|
|
44
|
+
status: 503,
|
|
45
|
+
reasonCode: 'deploy_preflight_busy',
|
|
46
|
+
details: { requestId, phase: 'admission', elapsedMs: Date.now() - startedAt },
|
|
43
47
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
error: `${accessMode} deployments require an authenticated deployer`,
|
|
48
|
+
res.setHeader('retry-after', '5');
|
|
49
|
+
return sendJson(res, 503, {
|
|
50
|
+
code: 'deploy_preflight_busy',
|
|
51
|
+
phase: 'admission',
|
|
52
|
+
error: 'This instance is already checking a deployment. Retry once after the indicated delay.',
|
|
50
53
|
});
|
|
51
54
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
activePreflights.add(registry);
|
|
56
|
+
let clientDisconnected = false;
|
|
57
|
+
let deadlineExceeded = false;
|
|
58
|
+
let phase = 'body';
|
|
59
|
+
let timer;
|
|
60
|
+
const onClose = () => {
|
|
61
|
+
if (!res.writableFinished)
|
|
62
|
+
clientDisconnected = true;
|
|
63
|
+
};
|
|
64
|
+
res.once('close', onClose);
|
|
65
|
+
try {
|
|
66
|
+
await audit.emit({
|
|
67
|
+
eventType: 'deploy.preflight.started',
|
|
68
|
+
...tenant,
|
|
69
|
+
actorSubject: identity.subject,
|
|
70
|
+
decision: 'allow',
|
|
71
|
+
details: { requestId, phase, elapsedMs: Date.now() - startedAt },
|
|
59
72
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
const body = await readDeployBody(req, maxBody);
|
|
74
|
+
if (req.aborted || res.destroyed)
|
|
75
|
+
return;
|
|
76
|
+
if (!body.ok)
|
|
77
|
+
return sendJson(res, body.status, { error: body.error });
|
|
78
|
+
phase = 'validation';
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
const raw = JSON.parse(body.text);
|
|
82
|
+
const result = deployPreflightRequestSchema.safeParse(raw);
|
|
83
|
+
if (!result.success)
|
|
84
|
+
throw new Error(formatWireError(result.error));
|
|
85
|
+
parsed = result.data;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
return sendJson(res, 400, {
|
|
89
|
+
code: 'invalid_deploy_request',
|
|
90
|
+
error: `invalid deploy preflight request: ${error.message}`,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const accessMode = parsed.accessMode ?? 'owner-only';
|
|
94
|
+
if (isIdentityAccessMode(accessMode) && identity === undefined) {
|
|
95
|
+
return sendJson(res, 401, {
|
|
96
|
+
code: 'deployer_identity_required',
|
|
97
|
+
error: `${accessMode} deployments require an authenticated deployer`,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const ownerSelection = await authorizeDeploymentOwnerSelection(res, controlPlane, tenant.org, accessMode, identity, parsed.ownerSubject);
|
|
101
|
+
if (!ownerSelection.ok)
|
|
102
|
+
return;
|
|
103
|
+
if (accessMode === 'public' && manifestUsesUserRoot(parsed.manifest)) {
|
|
104
|
+
return sendJson(res, 400, {
|
|
105
|
+
code: 'public_user_context_conflict',
|
|
106
|
+
error: 'public access mode cannot reference ${user}; use mixed for optional identity',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const [cspFault] = cspFaultsInManifest(parsed.manifest);
|
|
110
|
+
if (cspFault !== undefined) {
|
|
111
|
+
return sendJson(res, 400, {
|
|
112
|
+
code: 'invalid_widget_csp',
|
|
113
|
+
error: `widget "${cspFault.widget}" CSP ${cspFault.list} origin "${cspFault.value}" is not an ` +
|
|
114
|
+
`absolute https:// origin and would be dropped by the host renderer` +
|
|
115
|
+
(cspFault.suggestion !== undefined ? `; use "${cspFault.suggestion}"` : ''),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
phase = 'checks';
|
|
119
|
+
timer = setTimeout(() => {
|
|
120
|
+
deadlineExceeded = true;
|
|
121
|
+
if (!res.headersSent && !res.destroyed)
|
|
122
|
+
sendJson(res, 504, {
|
|
123
|
+
code: 'deploy_preflight_timeout',
|
|
124
|
+
phase,
|
|
125
|
+
error: `Preflight exceeded its ${DEPLOY_PREFLIGHT_CHECK_TIMEOUT_MS}ms checking budget. Publication did not start.`,
|
|
126
|
+
});
|
|
127
|
+
}, DEPLOY_PREFLIGHT_CHECK_TIMEOUT_MS);
|
|
128
|
+
const pending = [
|
|
129
|
+
registry.getApp(tenant.org, tenant.app),
|
|
130
|
+
registry.getEnvironment(tenant.org, tenant.app, tenant.env),
|
|
131
|
+
registry.preflightDeploy(tenant, parsed.manifest, {
|
|
132
|
+
...(parsed.connectors !== undefined ? { connectors: parsed.connectors } : {}),
|
|
133
|
+
actor: identity,
|
|
134
|
+
accessMode,
|
|
135
|
+
ownerSubject: ownerSelection.ownerSubject,
|
|
136
|
+
...(parsed.orgMembershipSources !== undefined
|
|
137
|
+
? { orgMembershipSources: parsed.orgMembershipSources }
|
|
138
|
+
: {}),
|
|
139
|
+
...(parsed.hostedAssets !== undefined ? { hostedAssets: parsed.hostedAssets } : {}),
|
|
140
|
+
...(parsed.serverVersion !== undefined ? { serverVersion: parsed.serverVersion } : {}),
|
|
141
|
+
deploymentSource: parsed.deploymentSource ?? 'cli',
|
|
142
|
+
}),
|
|
143
|
+
];
|
|
144
|
+
const checked = await Promise.all(pending)
|
|
145
|
+
.catch(async (error) => {
|
|
146
|
+
// Keep the resource slot until every already-started check settles, including after a timeout.
|
|
147
|
+
await Promise.allSettled(pending);
|
|
148
|
+
throw error;
|
|
149
|
+
})
|
|
150
|
+
.finally(() => clearTimeout(timer));
|
|
151
|
+
const [application, environment, preflight] = checked;
|
|
152
|
+
const errors = preflight.ok ? [] : preflight.errors;
|
|
153
|
+
const missingSecrets = configNames(errors, 'missing_secret', 'secrets.');
|
|
154
|
+
const missingVariables = configNames(errors, 'missing_variable', 'variables.');
|
|
155
|
+
const appState = application === undefined ? 'will-create' : 'existing';
|
|
156
|
+
const environmentState = environment === undefined ? 'will-create' : 'existing';
|
|
157
|
+
const ready = errors.length === 0;
|
|
158
|
+
const codes = [...new Set(errors.map((error) => error.code))].sort().join(',');
|
|
159
|
+
await audit.emit({
|
|
160
|
+
eventType: 'deploy.preflight.checked',
|
|
161
|
+
org: tenant.org,
|
|
162
|
+
app: tenant.app,
|
|
163
|
+
env: tenant.env,
|
|
164
|
+
decision: 'allow',
|
|
165
|
+
status: 200,
|
|
166
|
+
actorSubject: identity.subject,
|
|
167
|
+
...(identity.email !== undefined ? { actorEmail: identity.email } : {}),
|
|
168
|
+
details: {
|
|
169
|
+
requestId,
|
|
170
|
+
phase,
|
|
171
|
+
elapsedMs: Date.now() - startedAt,
|
|
172
|
+
clientDisconnected,
|
|
173
|
+
deadlineExceeded,
|
|
174
|
+
ready,
|
|
175
|
+
appState,
|
|
176
|
+
environmentState,
|
|
177
|
+
...(ownerSelection.ownerSubject !== undefined
|
|
178
|
+
? { ownerSubject: ownerSelection.ownerSubject }
|
|
179
|
+
: {}),
|
|
180
|
+
errorCount: errors.length,
|
|
181
|
+
codes,
|
|
182
|
+
missingSecretCount: missingSecrets.length,
|
|
183
|
+
missingVariableCount: missingVariables.length,
|
|
184
|
+
},
|
|
68
185
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
registry.preflightDeploy(tenant, parsed.manifest, {
|
|
74
|
-
...(parsed.connectors !== undefined ? { connectors: parsed.connectors } : {}),
|
|
75
|
-
actor: identity,
|
|
76
|
-
accessMode,
|
|
77
|
-
ownerSubject: ownerSelection.ownerSubject,
|
|
78
|
-
...(parsed.orgMembershipSources !== undefined
|
|
79
|
-
? { orgMembershipSources: parsed.orgMembershipSources }
|
|
80
|
-
: {}),
|
|
81
|
-
...(parsed.hostedAssets !== undefined ? { hostedAssets: parsed.hostedAssets } : {}),
|
|
82
|
-
...(parsed.serverVersion !== undefined ? { serverVersion: parsed.serverVersion } : {}),
|
|
83
|
-
deploymentSource: parsed.deploymentSource ?? 'cli',
|
|
84
|
-
}),
|
|
85
|
-
]);
|
|
86
|
-
const errors = preflight.ok ? [] : preflight.errors;
|
|
87
|
-
const missingSecrets = configNames(errors, 'missing_secret', 'secrets.');
|
|
88
|
-
const missingVariables = configNames(errors, 'missing_variable', 'variables.');
|
|
89
|
-
const appState = application === undefined ? 'will-create' : 'existing';
|
|
90
|
-
const environmentState = environment === undefined ? 'will-create' : 'existing';
|
|
91
|
-
const ready = errors.length === 0;
|
|
92
|
-
const codes = [...new Set(errors.map((error) => error.code))].sort().join(',');
|
|
93
|
-
await audit.emit({
|
|
94
|
-
eventType: 'deploy.preflight.checked',
|
|
95
|
-
org: tenant.org,
|
|
96
|
-
app: tenant.app,
|
|
97
|
-
env: tenant.env,
|
|
98
|
-
decision: 'allow',
|
|
99
|
-
status: 200,
|
|
100
|
-
actorSubject: identity.subject,
|
|
101
|
-
...(identity.email !== undefined ? { actorEmail: identity.email } : {}),
|
|
102
|
-
details: {
|
|
186
|
+
if (deadlineExceeded || clientDisconnected || res.destroyed)
|
|
187
|
+
return;
|
|
188
|
+
return sendJson(res, 200, {
|
|
189
|
+
ok: true,
|
|
103
190
|
ready,
|
|
104
|
-
appState,
|
|
105
|
-
environmentState,
|
|
106
191
|
...(ownerSelection.ownerSubject !== undefined
|
|
107
192
|
? { ownerSubject: ownerSelection.ownerSubject }
|
|
108
193
|
: {}),
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
194
|
+
target: {
|
|
195
|
+
...tenant,
|
|
196
|
+
appState,
|
|
197
|
+
environmentState,
|
|
198
|
+
},
|
|
199
|
+
config: {
|
|
200
|
+
ready: missingSecrets.length === 0 && missingVariables.length === 0,
|
|
201
|
+
missingSecrets,
|
|
202
|
+
missingVariables,
|
|
203
|
+
},
|
|
204
|
+
errors,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
await audit.emit({
|
|
209
|
+
eventType: 'deploy.preflight.failed',
|
|
122
210
|
...tenant,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
211
|
+
actorSubject: identity.subject,
|
|
212
|
+
decision: 'deny',
|
|
213
|
+
status: res.headersSent ? res.statusCode : 500,
|
|
214
|
+
reasonCode: 'preflight_failed',
|
|
215
|
+
details: {
|
|
216
|
+
requestId,
|
|
217
|
+
phase,
|
|
218
|
+
elapsedMs: Date.now() - startedAt,
|
|
219
|
+
clientDisconnected,
|
|
220
|
+
deadlineExceeded,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
clearTimeout(timer);
|
|
227
|
+
res.removeListener('close', onClose);
|
|
228
|
+
activePreflights.delete(registry);
|
|
229
|
+
}
|
|
133
230
|
}
|
|
134
231
|
function configNames(errors, code, prefix) {
|
|
135
232
|
return [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
41
41
|
"@noodle-borg/admission-limits": "0.0.0",
|
|
42
|
-
"@noodle-borg/agent-kit": "0.
|
|
42
|
+
"@noodle-borg/agent-kit": "0.94.0",
|
|
43
43
|
"@noodle-borg/app-package": "0.0.0",
|
|
44
44
|
"@noodle-borg/assistant-gateway": "0.0.0",
|
|
45
45
|
"@noodle-borg/auth": "0.0.0",
|
|
@@ -137,6 +137,9 @@ export declare const deployErrorResponseSchema: z.ZodObject<{
|
|
|
137
137
|
errors: z.ZodOptional<z.ZodUnknown>;
|
|
138
138
|
}, z.core.$strip>;
|
|
139
139
|
export type DeployErrorResponse = z.infer<typeof deployErrorResponseSchema>;
|
|
140
|
+
/** Checking budget after upload/authorization; the client allows additional upload/response time. */
|
|
141
|
+
export declare const DEPLOY_PREFLIGHT_CHECK_TIMEOUT_MS = 60000;
|
|
142
|
+
export declare const DEPLOY_PREFLIGHT_CLIENT_TIMEOUT_MS = 75000;
|
|
140
143
|
/**
|
|
141
144
|
* `POST /v1/orgs/{org}/apps/{app}/envs/{env}/deploy/preflight` accepts the same
|
|
142
145
|
* declarative input as deploy, but performs no deployment or asset upload.
|
|
@@ -109,6 +109,9 @@ export const deployErrorResponseSchema = z.object({
|
|
|
109
109
|
errors: z.unknown().optional(),
|
|
110
110
|
});
|
|
111
111
|
// ─── Deploy preflight ─────────────────────────────────────────────────────────────
|
|
112
|
+
/** Checking budget after upload/authorization; the client allows additional upload/response time. */
|
|
113
|
+
export const DEPLOY_PREFLIGHT_CHECK_TIMEOUT_MS = 60_000;
|
|
114
|
+
export const DEPLOY_PREFLIGHT_CLIENT_TIMEOUT_MS = 75_000;
|
|
112
115
|
/**
|
|
113
116
|
* `POST /v1/orgs/{org}/apps/{app}/envs/{env}/deploy/preflight` accepts the same
|
|
114
117
|
* declarative input as deploy, but performs no deployment or asset upload.
|
|
@@ -6,7 +6,8 @@ The package exports the canonical `<noodle-assistant>` managed Web Component, th
|
|
|
6
6
|
`<noodle-app-view>` MCP App host from `@noodleseed/assistant/app-view`, a managed React wrapper and
|
|
7
7
|
`NoodleAppView` adapter from `@noodleseed/assistant/react`, a renderer-free React hook from
|
|
8
8
|
`@noodleseed/assistant/react/client`, a DOM-free client from `@noodleseed/assistant/client`, and the
|
|
9
|
-
backend-only `
|
|
9
|
+
backend-only `createAssistantSessionHandler` and lower-level `createAssistantSession` exchange from
|
|
10
|
+
`@noodleseed/assistant/server`. The component inherits the
|
|
10
11
|
deployed MCP server's brand kit while slots, methods, events, and semantic CSS variables let a SaaS developer
|
|
11
12
|
integrate it without depending on internal DOM selectors. Light, dark, automatic host-page matching, and
|
|
12
13
|
host-page inversion work without replacing conversation or App DOM.
|
|
@@ -193,32 +194,29 @@ noodle assistant clients create --name web --org acme --app support --env prod
|
|
|
193
194
|
|
|
194
195
|
The command saves `{ clientId, clientSecret }` to a mode-`0600` file and prints its path, never the secret.
|
|
195
196
|
Move those values to the customer backend's secret manager without printing or committing them. A framework
|
|
196
|
-
route can
|
|
197
|
+
route can use the maintained same-origin handler. Generate the route, authentication adapter, mount and
|
|
198
|
+
contract tests with `noodle assistant embed --framework nextjs --surface authenticated` in the existing
|
|
199
|
+
application. Implement only `authenticateAssistantRequest` using its existing session and membership checks:
|
|
197
200
|
|
|
198
201
|
```ts
|
|
199
|
-
import {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
user: {
|
|
210
|
-
id: user.id,
|
|
211
|
-
email: user.email,
|
|
212
|
-
roles: user.roles,
|
|
213
|
-
scopes: user.scopes,
|
|
214
|
-
},
|
|
215
|
-
context,
|
|
216
|
-
preferences: { locale: user.locale, timeZone: user.timeZone },
|
|
217
|
-
});
|
|
218
|
-
return Response.json(session);
|
|
219
|
-
}
|
|
202
|
+
import { createAssistantSessionHandler } from "@noodleseed/assistant/server";
|
|
203
|
+
import { authenticateAssistantRequest } from "../../../../lib/noodle-assistant-auth";
|
|
204
|
+
|
|
205
|
+
export const POST = createAssistantSessionHandler({
|
|
206
|
+
serviceUrl: process.env.NOODLE_SERVICE_URL,
|
|
207
|
+
clientId: process.env.NOODLE_ASSISTANT_CLIENT_ID,
|
|
208
|
+
clientSecret: process.env.NOODLE_ASSISTANT_CLIENT_SECRET,
|
|
209
|
+
origin: process.env.PUBLIC_APP_ORIGIN,
|
|
210
|
+
authenticate: authenticateAssistantRequest,
|
|
211
|
+
});
|
|
220
212
|
```
|
|
221
213
|
|
|
214
|
+
The handler owns exact-Origin and JSON checks, signed-out JSON 401, bounded context/body parsing, no-store
|
|
215
|
+
responses, a 15-second deadline, and sanitized exchange failures without retries or redirects. Your adapter
|
|
216
|
+
returns `AssistantSessionIdentity | null`; its `user`, claims, preferences and routing must be backend-verified.
|
|
217
|
+
Keep stricter application CSRF middleware. Run the generated contract suite and your real identity/tenant
|
|
218
|
+
acceptance tests; synthetic contract tests alone do not verify your login implementation.
|
|
219
|
+
|
|
222
220
|
For a connector that uses `customerEndpoint("customer_api", ...)`, the authenticated backend may also pass
|
|
223
221
|
`routing: { endpoints: { customer_api: account.clusterApiBaseUrl } }`. Resolve the URL from server-owned
|
|
224
222
|
user/account membership after authentication; never accept it from the browser, page context, session
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noodleseed/assistant",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.0",
|
|
4
4
|
"description": "Embed the Noodle Seed customer-branded assistant in your web app with managed or framework-owned UI, a framework-neutral MCP App host, a DOM-free client, and a backend session helper. Authoring and deploying the server is @noodleseed/one.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noodleseed/one",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.156.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Noodle CLI by Noodle Seed — author, run, and deploy declarative MCP servers. Embedding the assistant in your own web app is @noodleseed/assistant.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -205,7 +205,6 @@
|
|
|
205
205
|
"shebang-command",
|
|
206
206
|
"shebang-regex",
|
|
207
207
|
"statuses",
|
|
208
|
-
"tailwindcss",
|
|
209
208
|
"tldts",
|
|
210
209
|
"tldts-core",
|
|
211
210
|
"toidentifier",
|
|
@@ -236,7 +235,7 @@
|
|
|
236
235
|
"@modelcontextprotocol/client": "2.0.0",
|
|
237
236
|
"@modelcontextprotocol/server": "2.0.0",
|
|
238
237
|
"@noodle-borg/admission-limits": "0.0.0",
|
|
239
|
-
"@noodle-borg/agent-kit": "0.
|
|
238
|
+
"@noodle-borg/agent-kit": "0.94.0",
|
|
240
239
|
"@noodle-borg/app-audit": "0.0.0",
|
|
241
240
|
"@noodle-borg/app-package": "0.0.0",
|
|
242
241
|
"@noodle-borg/assistant-gateway": "0.0.0",
|
|
@@ -285,7 +284,9 @@
|
|
|
285
284
|
"zod-to-json-schema": "3.25.2"
|
|
286
285
|
},
|
|
287
286
|
"devDependencies": {
|
|
288
|
-
"@
|
|
287
|
+
"@noodleseed/assistant": "@noodleseed/assistant@file:///home/runner/work/noodle-borg/noodle-borg/packages/assistant",
|
|
288
|
+
"@types/node": "24.13.1",
|
|
289
|
+
"next": "16.2.9"
|
|
289
290
|
},
|
|
290
291
|
"optionalDependencies": {}
|
|
291
292
|
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { ProjectAgentTarget } from './project.js';
|
|
2
|
-
/** Default SaaS starter: the portable Apps surface plus customer identity and verified context. */
|
|
3
|
-
export declare function saasFiles(name: string, agentTargets: readonly ProjectAgentTarget[]): Record<string, string>;
|
|
4
|
-
//# sourceMappingURL=saas-scaffold-template.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"saas-scaffold-template.d.ts","sourceRoot":"","sources":["../src/saas-scaffold-template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGvD,mGAAmG;AACnG,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,SAAS,kBAAkB,EAAE,GAC1C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA6DxB"}
|