@astrale-os/cli 1.0.0-beta.3 → 1.0.0-beta.30
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 +80 -6
- package/dist/astrale.js +55471 -58674
- package/dist/public/connect-core.js +2158 -30706
- package/dist/public/keys/index.js +1016 -29541
- package/dist/public/paths/index.js +370 -28654
- package/dist/types/admin/contract.d.ts +26 -0
- package/dist/types/admin/instance/client.d.ts +20 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +6 -10
- package/dist/types/connection/auth.d.ts +3 -0
- package/dist/types/connection/call.d.ts +3 -0
- package/dist/types/connection/command.d.ts +27 -0
- package/dist/types/connection/credential.d.ts +14 -0
- package/dist/types/connection/errors.d.ts +1 -0
- package/dist/types/connection/exchange.d.ts +9 -0
- package/dist/types/connection/failure/classify.d.ts +2 -0
- package/dist/types/connection/failure/index.d.ts +5 -0
- package/dist/types/connection/failure/model.d.ts +25 -0
- package/dist/types/connection/failure/render.d.ts +3 -0
- package/dist/types/connection/index.d.ts +8 -0
- package/dist/types/connection/reasons.d.ts +37 -0
- package/dist/types/connection/self.d.ts +37 -0
- package/dist/types/connection/session.d.ts +31 -0
- package/dist/types/connection/target.d.ts +38 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/lib/admin-instance.d.ts +17 -0
- package/dist/types/lib/admin-target.d.ts +11 -36
- package/dist/types/lib/config.d.ts +13 -59
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +25 -173
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +30 -88
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/output.d.ts +1 -1
- package/dist/types/lib/proc.d.ts +43 -0
- package/dist/types/lib/self.d.ts +9 -0
- package/dist/types/lib/update.d.ts +109 -0
- package/dist/types/lib/validation.d.ts +4 -1
- package/dist/types/state/exchange-credentials.d.ts +6 -2
- package/dist/types/state/files.d.ts +2 -0
- package/dist/types/state/index.d.ts +3 -2
- package/dist/types/state/paths.d.ts +2 -0
- package/dist/types/state/session-routes.d.ts +10 -0
- package/package.json +16 -19
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/.spec/architecture.md +12 -5
- package/src/admin/__tests__/fixture.ts +25 -0
- package/src/admin/catalog/.spec/api.d.ts +1 -3
- package/src/admin/catalog/.spec/architecture.md +5 -4
- package/src/admin/catalog/__tests__/client.test.ts +143 -69
- package/src/admin/catalog/client.ts +40 -54
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/contract.ts +47 -0
- package/src/admin/graph/.spec/api.d.ts +4 -10
- package/src/admin/graph/nodes.ts +1 -1
- package/src/admin/instance/.spec/api.d.ts +5 -9
- package/src/admin/instance/.spec/architecture.md +7 -7
- package/src/admin/instance/__tests__/client.test.ts +157 -175
- package/src/admin/instance/client.ts +39 -128
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +12 -18
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +34 -0
- package/src/commands/__tests__/domain-install-operation.test.ts +30 -22
- package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
- package/src/commands/__tests__/domain-list.test.ts +5 -25
- package/src/commands/__tests__/install-direct.test.ts +115 -8
- package/src/commands/__tests__/install-identity-override.test.ts +6 -17
- package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
- package/src/commands/__tests__/instance-status.test.ts +139 -0
- package/src/commands/__tests__/introspect-parse.test.ts +80 -1
- package/src/commands/__tests__/logs.test.ts +250 -2
- package/src/commands/__tests__/read-commands.test.ts +118 -10
- package/src/commands/__tests__/token-ttl.test.ts +49 -4
- package/src/commands/__tests__/update.test.ts +25 -3
- package/src/commands/__tests__/view.test.ts +50 -2
- package/src/commands/auth/logout.ts +2 -1
- package/src/commands/browser.ts +29 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +73 -46
- package/src/commands/domain/list.ts +1 -1
- package/src/commands/domain/publish.ts +14 -11
- package/src/commands/domain/uninstall.ts +2 -2
- package/src/commands/get.ts +15 -16
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +8 -8
- package/src/commands/instance/create.ts +8 -16
- package/src/commands/instance/delete.ts +12 -9
- package/src/commands/instance/list.ts +2 -2
- package/src/commands/instance/status.ts +66 -20
- package/src/commands/instance/use.ts +16 -12
- package/src/commands/introspect.ts +53 -41
- package/src/commands/logs.ts +123 -36
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +35 -32
- package/src/commands/session/analyze.ts +26 -4
- package/src/commands/token.ts +42 -27
- package/src/commands/ui/__tests__/commands.test.ts +152 -0
- package/src/commands/ui/add.ts +51 -0
- package/src/commands/ui/doctor.ts +21 -0
- package/src/commands/ui/init.ts +38 -0
- package/src/commands/ui/list.ts +26 -0
- package/src/commands/ui/preset-apply.ts +19 -0
- package/src/commands/ui/preset-list.ts +12 -0
- package/src/commands/ui/shared.ts +25 -0
- package/src/commands/update.ts +89 -48
- package/src/commands/view.ts +82 -37
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +14 -5
- package/src/connection/.spec/flows/session.ts +2 -2
- package/src/connection/.spec/laws/connection.ts +6 -2
- package/src/connection/.spec/layout.ts +1 -0
- package/src/connection/__tests__/auth.test.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +21 -1
- package/src/connection/__tests__/errors.test.ts +27 -20
- package/src/connection/__tests__/exchange.test.ts +64 -11
- package/src/connection/__tests__/failure-fixtures.ts +39 -0
- package/src/connection/__tests__/failure-safety.test.ts +75 -0
- package/src/connection/__tests__/self.test.ts +31 -11
- package/src/connection/__tests__/session.test.ts +8 -4
- package/src/connection/__tests__/target.test.ts +2 -0
- package/src/connection/auth.ts +13 -0
- package/src/connection/call.ts +1 -1
- package/src/connection/credential.ts +2 -2
- package/src/connection/errors.ts +1 -355
- package/src/connection/exchange.ts +110 -61
- package/src/connection/failure/classify.ts +110 -0
- package/src/connection/failure/index.ts +18 -0
- package/src/connection/failure/model.ts +28 -0
- package/src/connection/failure/render.ts +116 -0
- package/src/connection/reasons.ts +16 -0
- package/src/connection/self.ts +39 -21
- package/src/connection/session.ts +27 -9
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +12 -5
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +25 -6
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +21 -32
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/fixtures/registry-journey.ts +13 -1
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/__tests__/registry.test.ts +4 -0
- package/src/identity/registration.ts +3 -3
- package/src/identity/registry.ts +2 -0
- package/src/keys/__tests__/keys.test.ts +4 -0
- package/src/keys/credential.ts +1 -0
- package/src/lib/__tests__/admin-target.test.ts +12 -0
- package/src/lib/__tests__/binary.test.ts +16 -1
- package/src/lib/__tests__/browser-retention.test.ts +212 -0
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/config.test.ts +27 -0
- package/src/lib/__tests__/domain-publication.test.ts +10 -16
- package/src/lib/__tests__/idp.test.ts +34 -4
- package/src/lib/__tests__/instance-candidates.test.ts +1 -3
- package/src/lib/__tests__/instance-target.test.ts +7 -39
- package/src/lib/__tests__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/skills.test.ts +809 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
- package/src/lib/__tests__/update.test.ts +264 -11
- package/src/lib/__tests__/view-assets.test.ts +25 -0
- package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
- package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +26 -3
- package/src/lib/__tests__/view-session.test.ts +2 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/binary.ts +22 -5
- package/src/lib/browser-retention.ts +210 -0
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/config.ts +12 -1
- package/src/lib/domain-publication.ts +9 -7
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +34 -3
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +16 -11
- package/src/lib/log.ts +16 -9
- package/src/lib/output.ts +1 -1
- package/src/lib/proc.ts +7 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/skills/lock.ts +117 -0
- package/src/lib/skills/sync.ts +814 -0
- package/src/lib/skills.ts +1 -52
- package/src/lib/update.ts +298 -45
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/assets.ts +11 -2
- package/src/lib/view/external-open-intent.ts +41 -0
- package/src/lib/view/external-open-origins.ts +37 -0
- package/src/lib/view/host-capabilities.ts +17 -0
- package/src/lib/view/resolve.ts +1 -1
- package/src/lib/view/server.ts +18 -3
- package/src/lib/view/session.ts +2 -0
- package/src/program/.spec/api.d.ts +1 -0
- package/src/program/__tests__/program.test.ts +163 -6
- package/src/program/argv.ts +14 -0
- package/src/program/build.ts +26 -4
- package/src/program/command.ts +1 -0
- package/src/program/index.ts +1 -0
- package/src/program/registry.ts +2 -1
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/src/setup/steps/skills.ts +40 -38
- package/src/state/.spec/api.d.ts +21 -2
- package/src/state/.spec/architecture.md +18 -4
- package/src/state/.spec/layout.ts +1 -0
- package/src/state/__tests__/exchange-credentials.test.ts +88 -42
- package/src/state/__tests__/files.test.ts +24 -1
- package/src/state/__tests__/fixtures/session-route-process.ts +93 -0
- package/src/state/__tests__/paths.test.ts +1 -0
- package/src/state/__tests__/session-routes.test.ts +138 -0
- package/src/state/exchange-credentials.ts +22 -9
- package/src/state/files.ts +41 -0
- package/src/state/index.ts +3 -1
- package/src/state/paths.ts +3 -0
- package/src/state/session-routes.ts +34 -0
- package/src/telemetry/__tests__/analyze-log.test.ts +38 -0
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/__tests__/retention.test.ts +267 -0
- package/src/telemetry/__tests__/settings.test.ts +102 -0
- package/src/telemetry/__tests__/store-scan.test.ts +128 -0
- package/src/telemetry/__tests__/trigger.test.ts +33 -4
- package/src/telemetry/analyze.ts +24 -2
- package/src/telemetry/recorder.ts +15 -4
- package/src/telemetry/retention.ts +176 -0
- package/src/telemetry/session.ts +18 -12
- package/src/telemetry/settings.ts +64 -11
- package/src/telemetry/store.ts +92 -9
- package/src/telemetry/trigger.ts +16 -28
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +14 -0
- package/src/ui/.spec/laws.ts +50 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +1742 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +89 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +1044 -0
- package/src/ui/project.ts +333 -0
- package/src/ui/release.ts +398 -0
- package/src/ui/runner.ts +18 -0
- package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
- package/studio/client/dist/assets/index-BFVFs_8x.js +81 -0
- package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
- package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
- package/studio/client/dist/index.html +4 -3
- package/studio/package.json +8 -14
- package/studio/server/agent/ask.test.ts +1 -1
- package/studio/server/agent/bridge/grant.test.ts +1 -1
- package/studio/server/agent/bridge/routes.test.ts +1 -1
- package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/gateway/token.test.ts +1 -1
- package/studio/server/agent/harness/gateway/token.ts +3 -3
- package/studio/server/agent/prompts/anchors.test.ts +41 -32
- package/studio/server/agent/prompts/anchors.ts +23 -51
- package/studio/server/agent/prompts/system.test.ts +4 -4
- package/studio/server/agent/prompts/system.ts +5 -5
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/agent/run/preparation.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api/context.ts +1 -1
- package/studio/server/api/deployment.ts +1 -1
- package/studio/server/api/views.ts +2 -2
- package/studio/server/api/workspace.ts +1 -1
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +1 -1
- package/studio/server/cache.test.ts +24 -18
- package/studio/server/cache.ts +10 -54
- package/studio/server/cli-consumers.test.ts +31 -3
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +2 -2
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/probe.test.ts +1 -1
- package/studio/server/introspect/anatomy/views/routes.ts +46 -37
- package/studio/server/introspect/anatomy/views.ts +10 -16
- package/studio/server/introspect/anatomy-extras.test.ts +29 -56
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +6 -9
- package/studio/server/introspect/bundle.ts +0 -3
- package/studio/server/introspect/canonical-schema.test.ts +155 -424
- package/studio/server/introspect/canonical-schema.ts +248 -601
- package/studio/server/introspect/core.ts +14 -22
- package/studio/server/introspect/diff.test.ts +21 -9
- package/studio/server/introspect/diff.ts +19 -64
- package/studio/server/introspect/extractor.ts +23 -122
- package/studio/server/introspect/overlay-tsmorph.test.ts +116 -241
- package/studio/server/introspect/overlay-tsmorph.ts +0 -1
- package/studio/server/introspect/overlay.ts +3 -51
- package/studio/server/introspect/revision.test.ts +24 -28
- package/studio/server/introspect/revision.ts +11 -22
- package/studio/server/introspect/runtime.test.ts +77 -195
- package/studio/server/introspect/runtime.ts +5 -67
- package/studio/server/introspect/schema-ir-json.test.ts +81 -0
- package/studio/server/introspect/schema-ir-json.ts +39 -69
- package/studio/server/introspect/schema-refs.test.ts +43 -88
- package/studio/server/introspect/schema-refs.ts +14 -49
- package/studio/server/introspect/source-overlay/handlers.ts +116 -636
- package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
- package/studio/server/introspect/source-overlay/spans.ts +15 -45
- package/studio/server/lifecycle.ts +4 -1
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/documents.test.ts +69 -0
- package/studio/server/state/documents.ts +57 -10
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +18 -76
- package/studio/server/views/target.ts +24 -50
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace/updates.ts +23 -1
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/runtime.ts +4 -0
- package/studio/shared/contracts/schema.ts +44 -131
- package/studio/shared/contracts/surface.test.ts +15 -17
- package/studio/shared/contracts/workspace.ts +5 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -24
- package/studio/tsconfig.json +2 -1
- package/tsconfig.json +1 -1
- package/viewer/dist/index.html +21 -0
- package/viewer/dist/main.js +41 -62
- package/.check-workspace.cjs +0 -40
- package/src/admin/binding.ts +0 -168
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
- package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
- package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
- package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
- package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
- package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
- package/studio/server/introspect/core-extractor.ts +0 -139
- package/studio/server/introspect/overlay.test.ts +0 -72
- package/studio/server/introspect/source-overlay/annotations.ts +0 -14
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { access, lstat, readFile, realpath } from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { loadConfig } from 'tsconfig-paths'
|
|
4
|
+
|
|
5
|
+
import { UiError, type PackageManager } from './model'
|
|
6
|
+
|
|
7
|
+
export type UiProject = {
|
|
8
|
+
root: string
|
|
9
|
+
packageJsonPath: string
|
|
10
|
+
packageJson: Record<string, unknown>
|
|
11
|
+
manager: PackageManager
|
|
12
|
+
lockPath?: string
|
|
13
|
+
cssPath: string
|
|
14
|
+
componentsPath: string
|
|
15
|
+
uiLockPath: string
|
|
16
|
+
isAstraleDomain: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const MANAGERS: readonly [string, PackageManager][] = [
|
|
20
|
+
['pnpm-lock.yaml', 'pnpm'],
|
|
21
|
+
['bun.lock', 'bun'],
|
|
22
|
+
['bun.lockb', 'bun'],
|
|
23
|
+
['yarn.lock', 'yarn'],
|
|
24
|
+
['package-lock.json', 'npm'],
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
async function exists(target: string): Promise<boolean> {
|
|
28
|
+
return access(target).then(
|
|
29
|
+
() => true,
|
|
30
|
+
() => false,
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function readManifest(target: string): Promise<Record<string, unknown>> {
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(await readFile(target, 'utf8')) as Record<string, unknown>
|
|
37
|
+
} catch (cause) {
|
|
38
|
+
throw new UiError('UI_PROJECT_UNSUPPORTED', 'package.json is not valid JSON.', undefined, {
|
|
39
|
+
cause,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function pathPatternMatch(pattern: string, candidate: string): string | undefined {
|
|
45
|
+
const wildcard = pattern.indexOf('*')
|
|
46
|
+
if (wildcard === -1) return pattern === candidate ? '' : undefined
|
|
47
|
+
const prefix = pattern.slice(0, wildcard)
|
|
48
|
+
const suffix = pattern.slice(wildcard + 1)
|
|
49
|
+
if (!candidate.startsWith(prefix) || !candidate.endsWith(suffix)) return undefined
|
|
50
|
+
return candidate.slice(prefix.length, candidate.length - suffix.length)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolvePackageImport(project: UiProject, candidate: string): string | undefined {
|
|
54
|
+
const imports = project.packageJson.imports
|
|
55
|
+
if (!imports || typeof imports !== 'object' || Array.isArray(imports)) return undefined
|
|
56
|
+
const matches = Object.entries(imports as Record<string, unknown>)
|
|
57
|
+
.flatMap(([pattern, target]) => {
|
|
58
|
+
const wildcard = pathPatternMatch(pattern, candidate)
|
|
59
|
+
if (wildcard === undefined || typeof target !== 'string' || !target.startsWith('./'))
|
|
60
|
+
return []
|
|
61
|
+
const wildcardIndex = pattern.indexOf('*')
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
exact: wildcardIndex === -1,
|
|
65
|
+
prefixLength: wildcardIndex === -1 ? pattern.length : wildcardIndex,
|
|
66
|
+
suffixLength: wildcardIndex === -1 ? 0 : pattern.length - wildcardIndex - 1,
|
|
67
|
+
target: target.replaceAll('*', wildcard),
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
})
|
|
71
|
+
.sort(
|
|
72
|
+
(left, right) =>
|
|
73
|
+
Number(right.exact) - Number(left.exact) ||
|
|
74
|
+
right.prefixLength - left.prefixLength ||
|
|
75
|
+
right.suffixLength - left.suffixLength,
|
|
76
|
+
)
|
|
77
|
+
return matches[0] ? path.resolve(project.root, matches[0].target) : undefined
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function resolveAlias(project: UiProject, candidate: string): Promise<string | undefined> {
|
|
81
|
+
const config = loadConfig(project.root)
|
|
82
|
+
if (config.resultType === 'failed') return undefined
|
|
83
|
+
const matches: Array<{
|
|
84
|
+
exact: boolean
|
|
85
|
+
prefixLength: number
|
|
86
|
+
suffixLength: number
|
|
87
|
+
resolved: string
|
|
88
|
+
}> = []
|
|
89
|
+
for (const [pattern, replacements] of Object.entries(config.paths)) {
|
|
90
|
+
const wildcard = pathPatternMatch(pattern, candidate)
|
|
91
|
+
if (wildcard === undefined) continue
|
|
92
|
+
const replacement = replacements?.[0]
|
|
93
|
+
if (!replacement) continue
|
|
94
|
+
const wildcardIndex = pattern.indexOf('*')
|
|
95
|
+
matches.push({
|
|
96
|
+
exact: wildcardIndex === -1,
|
|
97
|
+
prefixLength: wildcardIndex === -1 ? pattern.length : wildcardIndex,
|
|
98
|
+
suffixLength: wildcardIndex === -1 ? 0 : pattern.length - wildcardIndex - 1,
|
|
99
|
+
resolved: path.resolve(config.absoluteBaseUrl, replacement.replaceAll('*', wildcard)),
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
matches.sort(
|
|
103
|
+
(left, right) =>
|
|
104
|
+
Number(right.exact) - Number(left.exact) ||
|
|
105
|
+
right.prefixLength - left.prefixLength ||
|
|
106
|
+
right.suffixLength - left.suffixLength,
|
|
107
|
+
)
|
|
108
|
+
const best = matches[0]
|
|
109
|
+
if (!best) return undefined
|
|
110
|
+
const ambiguous = matches.some(
|
|
111
|
+
(match) =>
|
|
112
|
+
match.exact === best.exact &&
|
|
113
|
+
match.prefixLength === best.prefixLength &&
|
|
114
|
+
match.suffixLength === best.suffixLength &&
|
|
115
|
+
match.resolved !== best.resolved,
|
|
116
|
+
)
|
|
117
|
+
if (ambiguous) {
|
|
118
|
+
throw new UiError(
|
|
119
|
+
'UI_PROJECT_UNSUPPORTED',
|
|
120
|
+
'The components alias resolves to conflicting project paths.',
|
|
121
|
+
'Keep one authoritative compilerOptions.paths mapping for the components alias.',
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
return best.resolved
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function resolveUiRegistryTarget(
|
|
128
|
+
project: UiProject,
|
|
129
|
+
declaredTarget: string,
|
|
130
|
+
): Promise<string> {
|
|
131
|
+
if (!declaredTarget.startsWith('components/')) return declaredTarget
|
|
132
|
+
const components = await readFile(project.componentsPath, 'utf8')
|
|
133
|
+
.then((value) => JSON.parse(value) as { aliases?: { components?: unknown } })
|
|
134
|
+
.catch(() => undefined)
|
|
135
|
+
const componentsAlias = components?.aliases?.components
|
|
136
|
+
if (typeof componentsAlias !== 'string' || componentsAlias.length === 0) return declaredTarget
|
|
137
|
+
const suffix = declaredTarget.slice('components/'.length)
|
|
138
|
+
const directAlias = /^(?:\.?\.?\/|src\/|app\/|frontend\/|components\/)/u.test(componentsAlias)
|
|
139
|
+
const resolved =
|
|
140
|
+
resolvePackageImport(project, componentsAlias) ??
|
|
141
|
+
(await resolveAlias(project, componentsAlias)) ??
|
|
142
|
+
(directAlias ? path.resolve(project.root, componentsAlias) : undefined)
|
|
143
|
+
if (!resolved) {
|
|
144
|
+
throw new UiError(
|
|
145
|
+
'UI_PROJECT_UNSUPPORTED',
|
|
146
|
+
'components.json alias cannot be resolved through tsconfig.json or jsconfig.json.',
|
|
147
|
+
'Define a matching compilerOptions.paths entry for ' + componentsAlias + '.',
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
const relative = path.relative(project.root, path.join(resolved, suffix))
|
|
151
|
+
if (relative === '..' || relative.startsWith('..' + path.sep) || path.isAbsolute(relative)) {
|
|
152
|
+
throw new UiError('UI_PROJECT_UNSUPPORTED', 'components.json alias escapes the project.')
|
|
153
|
+
}
|
|
154
|
+
return relative.split(path.sep).join('/')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function hasReactTailwind(manifest: Record<string, unknown>): boolean {
|
|
158
|
+
const dependencies = {
|
|
159
|
+
...(manifest.dependencies as Record<string, string> | undefined),
|
|
160
|
+
...(manifest.devDependencies as Record<string, string> | undefined),
|
|
161
|
+
...(manifest.peerDependencies as Record<string, string> | undefined),
|
|
162
|
+
}
|
|
163
|
+
return Boolean(dependencies.react && dependencies['react-dom'] && dependencies.tailwindcss)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function assertPhysicalProjectPath(root: string, target: string): Promise<void> {
|
|
167
|
+
const physicalRoot = await realpath(root)
|
|
168
|
+
let existing = target
|
|
169
|
+
while (!(await exists(existing))) {
|
|
170
|
+
const parent = path.dirname(existing)
|
|
171
|
+
if (parent === existing) break
|
|
172
|
+
existing = parent
|
|
173
|
+
}
|
|
174
|
+
const physicalTarget = await realpath(existing)
|
|
175
|
+
const relative = path.relative(physicalRoot, physicalTarget)
|
|
176
|
+
if (relative === '..' || relative.startsWith('..' + path.sep) || path.isAbsolute(relative)) {
|
|
177
|
+
throw new UiError('UI_PROJECT_UNSUPPORTED', 'components.json CSS path escapes the project.')
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export async function discoverUiProject(input = process.cwd()): Promise<UiProject> {
|
|
182
|
+
let root = path.resolve(input)
|
|
183
|
+
if (!(await exists(root))) {
|
|
184
|
+
throw new UiError('UI_PROJECT_UNSUPPORTED', 'Project path does not exist: ' + root)
|
|
185
|
+
}
|
|
186
|
+
if (!(await lstat(root)).isDirectory()) root = path.dirname(root)
|
|
187
|
+
|
|
188
|
+
while (true) {
|
|
189
|
+
const manifestPath = path.join(root, 'package.json')
|
|
190
|
+
if (await exists(manifestPath)) {
|
|
191
|
+
const manifest = await readManifest(manifestPath)
|
|
192
|
+
const parent = path.dirname(root)
|
|
193
|
+
if (hasReactTailwind(manifest) || parent === root) break
|
|
194
|
+
if (await exists(path.join(parent, 'package.json'))) {
|
|
195
|
+
root = parent
|
|
196
|
+
continue
|
|
197
|
+
}
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
const parent = path.dirname(root)
|
|
201
|
+
if (parent === root) {
|
|
202
|
+
throw new UiError(
|
|
203
|
+
'UI_PROJECT_UNSUPPORTED',
|
|
204
|
+
'No package.json found from ' + path.resolve(input),
|
|
205
|
+
'Run the command inside an existing React application or pass its path.',
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
root = parent
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const packageJsonPath = path.join(root, 'package.json')
|
|
212
|
+
const packageJson = await readManifest(packageJsonPath)
|
|
213
|
+
|
|
214
|
+
let manager: PackageManager = 'npm'
|
|
215
|
+
let lockPath: string | undefined
|
|
216
|
+
for (const [file, candidate] of MANAGERS) {
|
|
217
|
+
const target = path.join(root, file)
|
|
218
|
+
if (await exists(target)) {
|
|
219
|
+
manager = candidate
|
|
220
|
+
lockPath = target
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const declared = packageJson.packageManager
|
|
225
|
+
if (!lockPath && typeof declared === 'string') {
|
|
226
|
+
const candidate = declared.split('@')[0]
|
|
227
|
+
if (
|
|
228
|
+
candidate === 'pnpm' ||
|
|
229
|
+
candidate === 'npm' ||
|
|
230
|
+
candidate === 'yarn' ||
|
|
231
|
+
candidate === 'bun'
|
|
232
|
+
) {
|
|
233
|
+
manager = candidate
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (!lockPath) {
|
|
237
|
+
const expectedLock = {
|
|
238
|
+
pnpm: 'pnpm-lock.yaml',
|
|
239
|
+
npm: 'package-lock.json',
|
|
240
|
+
yarn: 'yarn.lock',
|
|
241
|
+
bun: 'bun.lock',
|
|
242
|
+
}[manager]
|
|
243
|
+
lockPath = path.join(root, expectedLock)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const rootCssCandidates = ['src/index.css', 'src/app.css', 'app/globals.css', 'src/styles.css']
|
|
247
|
+
const frontendCssCandidates = [
|
|
248
|
+
'frontend/src/index.css',
|
|
249
|
+
'frontend/src/app.css',
|
|
250
|
+
'frontend/src/styles.css',
|
|
251
|
+
]
|
|
252
|
+
const componentsPath = path.join(root, 'components.json')
|
|
253
|
+
const configuredCss = await readFile(componentsPath, 'utf8')
|
|
254
|
+
.then((value) => {
|
|
255
|
+
const components = JSON.parse(value) as { tailwind?: { css?: unknown } }
|
|
256
|
+
const css = components.tailwind?.css
|
|
257
|
+
if (typeof css !== 'string' || css.length === 0) return undefined
|
|
258
|
+
const target = path.resolve(root, css)
|
|
259
|
+
const relative = path.relative(root, target)
|
|
260
|
+
if (relative === '..' || relative.startsWith('..' + path.sep) || path.isAbsolute(relative)) {
|
|
261
|
+
throw new UiError('UI_PROJECT_UNSUPPORTED', 'components.json CSS path escapes the project.')
|
|
262
|
+
}
|
|
263
|
+
return { relative: relative.split(path.sep).join('/'), target }
|
|
264
|
+
})
|
|
265
|
+
.catch((error: unknown) => {
|
|
266
|
+
if (error instanceof UiError) throw error
|
|
267
|
+
return undefined
|
|
268
|
+
})
|
|
269
|
+
const configuredCssRelative = configuredCss?.relative
|
|
270
|
+
const cssCandidates =
|
|
271
|
+
configuredCssRelative !== undefined
|
|
272
|
+
? [configuredCssRelative]
|
|
273
|
+
: (await exists(path.join(root, 'frontend/package.json')))
|
|
274
|
+
? [...frontendCssCandidates, ...rootCssCandidates]
|
|
275
|
+
: [...rootCssCandidates, ...frontendCssCandidates]
|
|
276
|
+
const resolvedCss = await Promise.all(
|
|
277
|
+
cssCandidates.map(async (file) => ((await exists(path.join(root, file))) ? file : undefined)),
|
|
278
|
+
)
|
|
279
|
+
const cssRelative = configuredCssRelative ?? resolvedCss.find(Boolean) ?? 'src/astrale-ui.css'
|
|
280
|
+
await assertPhysicalProjectPath(root, path.join(root, cssRelative))
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
root,
|
|
284
|
+
packageJsonPath,
|
|
285
|
+
packageJson,
|
|
286
|
+
manager,
|
|
287
|
+
lockPath,
|
|
288
|
+
cssPath: path.join(root, cssRelative),
|
|
289
|
+
componentsPath,
|
|
290
|
+
uiLockPath: path.join(root, 'astrale-ui.lock.json'),
|
|
291
|
+
isAstraleDomain:
|
|
292
|
+
(await exists(path.join(root, 'astrale.config.ts'))) &&
|
|
293
|
+
(await exists(path.join(root, 'ui/index.ts'))) &&
|
|
294
|
+
(await exists(path.join(root, 'frontend/package.json'))),
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function assertSupportedUiProject(project: UiProject): void {
|
|
299
|
+
const manifest = project.packageJson
|
|
300
|
+
const dependencies = {
|
|
301
|
+
...(manifest.dependencies as Record<string, string> | undefined),
|
|
302
|
+
...(manifest.devDependencies as Record<string, string> | undefined),
|
|
303
|
+
...(manifest.peerDependencies as Record<string, string> | undefined),
|
|
304
|
+
}
|
|
305
|
+
if (!dependencies.react || !dependencies['react-dom']) {
|
|
306
|
+
throw new UiError(
|
|
307
|
+
'UI_PROJECT_UNSUPPORTED',
|
|
308
|
+
'Astrale UI requires an existing React application.',
|
|
309
|
+
'Install React first or choose a non-UI project scaffold.',
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
const tailwind = dependencies.tailwindcss
|
|
313
|
+
if (!tailwind || !/(?:^|[^0-9])4(?:\.|$)/u.test(tailwind)) {
|
|
314
|
+
throw new UiError(
|
|
315
|
+
'UI_PROJECT_UNSUPPORTED',
|
|
316
|
+
'Astrale UI V1 requires Tailwind CSS 4.',
|
|
317
|
+
'Upgrade Tailwind to v4 before initializing Astrale UI.',
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function projectRelative(project: UiProject, target: string): string {
|
|
323
|
+
const relative = path.relative(project.root, target)
|
|
324
|
+
if (
|
|
325
|
+
relative === '' ||
|
|
326
|
+
relative === '..' ||
|
|
327
|
+
relative.startsWith('..' + path.sep) ||
|
|
328
|
+
path.isAbsolute(relative)
|
|
329
|
+
) {
|
|
330
|
+
throw new UiError('UI_LOCK_INVALID', 'Path escapes the project root: ' + target)
|
|
331
|
+
}
|
|
332
|
+
return relative.split(path.sep).join('/')
|
|
333
|
+
}
|