@korajs/cli 1.0.0-beta.1 → 1.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +597 -291
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +123 -36
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-EOWLAAIV.js → chunk-4IKCHRJ6.js} +273 -57
- package/dist/chunk-4IKCHRJ6.js.map +1 -0
- package/dist/{chunk-B5YS4STN.js → chunk-G2PBYMMA.js} +2 -2
- package/dist/{chunk-B5YS4STN.js.map → chunk-G2PBYMMA.js.map} +1 -1
- package/dist/{chunk-BWTKRKNJ.js → chunk-Z7GCXMGW.js} +9 -5
- package/dist/chunk-Z7GCXMGW.js.map +1 -0
- package/dist/create.cjs +8 -4
- package/dist/create.cjs.map +1 -1
- package/dist/create.js +2 -2
- package/dist/index.cjs +389 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -1
- package/dist/index.d.ts +58 -1
- package/dist/index.js +12 -2
- package/dist/{spectator-manager-E2LH2P54.js → spectator-manager-FTH2NFSE.js} +5 -2
- package/dist/spectator-manager-FTH2NFSE.js.map +1 -0
- package/package.json +10 -8
- package/templates/react-basic/README.md.hbs +1 -1
- package/templates/react-basic/src/main.tsx +1 -1
- package/templates/react-basic/src/vite-env.d.ts +1 -0
- package/templates/react-basic/vite.config.ts +7 -4
- package/templates/react-sync/README.md.hbs +6 -5
- package/templates/react-sync/server.ts +46 -3
- package/templates/react-sync/src/main.tsx +1 -1
- package/templates/react-sync/src/vite-env.d.ts +1 -0
- package/templates/react-sync/vite.config.ts +14 -4
- package/templates/react-tailwind/README.md.hbs +1 -1
- package/templates/react-tailwind/src/main.tsx +1 -1
- package/templates/react-tailwind/src/vite-env.d.ts +1 -0
- package/templates/react-tailwind/vite.config.ts +7 -4
- package/templates/react-tailwind-sync/README.md.hbs +6 -5
- package/templates/react-tailwind-sync/server.ts +46 -3
- package/templates/react-tailwind-sync/src/main.tsx +1 -1
- package/templates/react-tailwind-sync/src/vite-env.d.ts +1 -0
- package/templates/react-tailwind-sync/vite.config.ts +14 -4
- package/templates/svelte-basic/README.md.hbs +1 -1
- package/templates/svelte-basic/src/main.ts +1 -1
- package/templates/svelte-basic/vite.config.ts +4 -2
- package/templates/svelte-sync/README.md.hbs +6 -5
- package/templates/svelte-sync/server.ts +46 -3
- package/templates/svelte-sync/src/main.ts +1 -1
- package/templates/svelte-sync/src/vite-env.d.ts +1 -0
- package/templates/svelte-sync/vite.config.ts +11 -2
- package/templates/svelte-tailwind/README.md.hbs +1 -1
- package/templates/svelte-tailwind/src/main.ts +1 -1
- package/templates/svelte-tailwind/vite.config.ts +4 -2
- package/templates/svelte-tailwind-sync/README.md.hbs +6 -5
- package/templates/svelte-tailwind-sync/server.ts +46 -3
- package/templates/svelte-tailwind-sync/src/main.ts +1 -1
- package/templates/svelte-tailwind-sync/src/vite-env.d.ts +1 -0
- package/templates/svelte-tailwind-sync/vite.config.ts +11 -2
- package/templates/tauri-react/server.ts +44 -1
- package/templates/tauri-react/src/AppShell.tsx +1 -1
- package/templates/tauri-react/src/vite-env.d.ts +1 -0
- package/templates/vue-basic/README.md.hbs +1 -1
- package/templates/vue-basic/src/main.ts +1 -1
- package/templates/vue-basic/vite.config.ts +4 -2
- package/templates/vue-sync/README.md.hbs +6 -5
- package/templates/vue-sync/server.ts +46 -3
- package/templates/vue-sync/src/main.ts +1 -1
- package/templates/vue-sync/src/vite-env.d.ts +1 -0
- package/templates/vue-sync/vite.config.ts +11 -2
- package/templates/vue-tailwind/README.md.hbs +1 -1
- package/templates/vue-tailwind/src/main.ts +1 -1
- package/templates/vue-tailwind/vite.config.ts +4 -2
- package/templates/vue-tailwind-sync/README.md.hbs +6 -5
- package/templates/vue-tailwind-sync/server.ts +46 -3
- package/templates/vue-tailwind-sync/src/main.ts +1 -1
- package/templates/vue-tailwind-sync/src/vite-env.d.ts +1 -0
- package/templates/vue-tailwind-sync/vite.config.ts +11 -2
- package/dist/chunk-BWTKRKNJ.js.map +0 -1
- package/dist/chunk-EOWLAAIV.js.map +0 -1
- package/dist/spectator-manager-E2LH2P54.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -59,6 +59,63 @@ declare class DevServerError extends KoraError {
|
|
|
59
59
|
constructor(binary: string, searchPath: string);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Writes an AGENTS.md into an existing project so AI coding agents follow Kora
|
|
64
|
+
* conventions. Refuses to overwrite an existing file unless `--force` is passed.
|
|
65
|
+
*/
|
|
66
|
+
declare const agentsCommand: citty.CommandDef<{
|
|
67
|
+
force: {
|
|
68
|
+
type: "boolean";
|
|
69
|
+
description: string;
|
|
70
|
+
default: false;
|
|
71
|
+
};
|
|
72
|
+
framework: {
|
|
73
|
+
type: "string";
|
|
74
|
+
description: string;
|
|
75
|
+
};
|
|
76
|
+
}>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Canonical AGENTS.md generator.
|
|
80
|
+
*
|
|
81
|
+
* `kora agents-md` writes this file into an existing project so AI coding agents
|
|
82
|
+
* (and humans) build Kora apps correctly instead of drifting to fetch calls,
|
|
83
|
+
* hand-written types, and loading spinners. New projects scaffolded with
|
|
84
|
+
* `create-kora-app` already ship an AGENTS.md; this command covers the manual
|
|
85
|
+
* setup path and lets existing projects regenerate the guidance on demand.
|
|
86
|
+
*/
|
|
87
|
+
/** UI framework whose bindings section the generated AGENTS.md documents. */
|
|
88
|
+
type AgentsFramework = 'react' | 'vue' | 'svelte';
|
|
89
|
+
/** All supported frameworks, for validation and help text. */
|
|
90
|
+
declare const AGENTS_FRAMEWORKS: readonly AgentsFramework[];
|
|
91
|
+
/**
|
|
92
|
+
* Narrows an arbitrary string to a supported {@link AgentsFramework}.
|
|
93
|
+
*
|
|
94
|
+
* @param value - Candidate framework identifier
|
|
95
|
+
* @returns `true` when `value` is one of the supported frameworks
|
|
96
|
+
*/
|
|
97
|
+
declare function isAgentsFramework(value: string): value is AgentsFramework;
|
|
98
|
+
/**
|
|
99
|
+
* Detects the UI framework from a project's package manifest by looking for the
|
|
100
|
+
* matching `@korajs/*` binding package in dependencies or devDependencies.
|
|
101
|
+
*
|
|
102
|
+
* @param manifest - Parsed `package.json` contents
|
|
103
|
+
* @returns The detected framework, or `null` when no binding package is present
|
|
104
|
+
*/
|
|
105
|
+
declare function detectFrameworkFromManifest(manifest: unknown): AgentsFramework | null;
|
|
106
|
+
/**
|
|
107
|
+
* Renders the full AGENTS.md document for the given framework.
|
|
108
|
+
*
|
|
109
|
+
* The body (golden rules, data API cheat sheet, conflict handling, sync notes,
|
|
110
|
+
* commands) is framework-agnostic; only the bindings section changes. File paths
|
|
111
|
+
* are described as conventions so the guidance is correct for both scaffolded and
|
|
112
|
+
* manually configured projects.
|
|
113
|
+
*
|
|
114
|
+
* @param framework - UI framework whose bindings to document
|
|
115
|
+
* @returns The complete AGENTS.md file contents, terminated with a trailing newline
|
|
116
|
+
*/
|
|
117
|
+
declare function generateAgentsMd(framework: AgentsFramework): string;
|
|
118
|
+
|
|
62
119
|
/**
|
|
63
120
|
* Generates TypeScript interfaces from a SchemaDefinition.
|
|
64
121
|
* For each collection, produces three interfaces:
|
|
@@ -692,4 +749,4 @@ interface SyncProviderPresetOptions {
|
|
|
692
749
|
*/
|
|
693
750
|
declare function applySyncProviderPreset(options: SyncProviderPresetOptions): Promise<void>;
|
|
694
751
|
|
|
695
|
-
export { type AuthOption, type BuildArtifacts, ClackPromptClient, CliError, type ClientBuildOptions, type ClientBuildResult, type ContextAwareDeployAdapter, type CreateFlags, type CreatePreferences, DEPLOY_PLATFORMS, type DatabaseOption, type DatabaseProviderOption, type DeployAdapter, type DeployPlatform, type DeployResult, type DeployState, type DeployStateCreateInput, type DeployStatePatch, type DeploymentStatus, DevServerError, type DockerfileOptions, FlyAdapter, type FlyAdapterContext, type FlyAdapterOptions, type FlyCommandResult, type FlyCommandRunner, type FlyTomlOptions, type FrameworkOption, InvalidProjectError, type LogLine, type LogOptions, NodeFlyCommandRunner, NodeRailwayCommandRunner, PACKAGE_MANAGERS, type PackageManager, type PreferenceResolutionResult, PreferenceStore, type ProjectConfig, ProjectExistsError, type ProjectNameValidationResult, PromptCancelledError, type PromptClient, type ProvisionResult, RailwayAdapter, type RailwayAdapterContext, type RailwayAdapterOptions, type RailwayCommandResult, type RailwayCommandRunner, type RailwayJsonOptions, ReadlinePromptClient, SchemaNotFoundError, type SelectOption, type ServerBundleOptions, type ServerBundleResult, StubDeployAdapter, TEMPLATES, TEMPLATE_INFO, type TemplateContext, type TemplateInfo, type TemplateName, type TemplateSelectionInput, applySyncProviderPreset, buildClient, bundleServer, createDeployAdapter, createPromptClient, deployCommand, determineTemplateFromSelections, generateDeployPackageJson, generateDockerIgnore, generateDockerfile, generateFlyToml, generateRailwayJson, generateTypes, getCreatePreferencesOrDefault, getDefaultCreatePreferences, isAuthValue, isDatabaseProviderValue, isDatabaseValue, isDeployPlatform, isFrameworkValue, readDeployState, resetDeployState, resolveCreatePreferencesFlow, resolveDeployDirectory, resolveDeployStatePath, saveResolvedPreferences, shouldSavePreferences, updateDeployState, validateProjectName, writeDeployState, writeDockerIgnoreArtifact, writeDockerfileArtifact, writeFlyTomlArtifact, writeRailwayJsonArtifact };
|
|
752
|
+
export { AGENTS_FRAMEWORKS, type AgentsFramework, type AuthOption, type BuildArtifacts, ClackPromptClient, CliError, type ClientBuildOptions, type ClientBuildResult, type ContextAwareDeployAdapter, type CreateFlags, type CreatePreferences, DEPLOY_PLATFORMS, type DatabaseOption, type DatabaseProviderOption, type DeployAdapter, type DeployPlatform, type DeployResult, type DeployState, type DeployStateCreateInput, type DeployStatePatch, type DeploymentStatus, DevServerError, type DockerfileOptions, FlyAdapter, type FlyAdapterContext, type FlyAdapterOptions, type FlyCommandResult, type FlyCommandRunner, type FlyTomlOptions, type FrameworkOption, InvalidProjectError, type LogLine, type LogOptions, NodeFlyCommandRunner, NodeRailwayCommandRunner, PACKAGE_MANAGERS, type PackageManager, type PreferenceResolutionResult, PreferenceStore, type ProjectConfig, ProjectExistsError, type ProjectNameValidationResult, PromptCancelledError, type PromptClient, type ProvisionResult, RailwayAdapter, type RailwayAdapterContext, type RailwayAdapterOptions, type RailwayCommandResult, type RailwayCommandRunner, type RailwayJsonOptions, ReadlinePromptClient, SchemaNotFoundError, type SelectOption, type ServerBundleOptions, type ServerBundleResult, StubDeployAdapter, TEMPLATES, TEMPLATE_INFO, type TemplateContext, type TemplateInfo, type TemplateName, type TemplateSelectionInput, agentsCommand, applySyncProviderPreset, buildClient, bundleServer, createDeployAdapter, createPromptClient, deployCommand, detectFrameworkFromManifest, determineTemplateFromSelections, generateAgentsMd, generateDeployPackageJson, generateDockerIgnore, generateDockerfile, generateFlyToml, generateRailwayJson, generateTypes, getCreatePreferencesOrDefault, getDefaultCreatePreferences, isAgentsFramework, isAuthValue, isDatabaseProviderValue, isDatabaseValue, isDeployPlatform, isFrameworkValue, readDeployState, resetDeployState, resolveCreatePreferencesFlow, resolveDeployDirectory, resolveDeployStatePath, saveResolvedPreferences, shouldSavePreferences, updateDeployState, validateProjectName, writeDeployState, writeDockerIgnoreArtifact, writeDockerfileArtifact, writeFlyTomlArtifact, writeRailwayJsonArtifact };
|
package/dist/index.d.ts
CHANGED
|
@@ -59,6 +59,63 @@ declare class DevServerError extends KoraError {
|
|
|
59
59
|
constructor(binary: string, searchPath: string);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Writes an AGENTS.md into an existing project so AI coding agents follow Kora
|
|
64
|
+
* conventions. Refuses to overwrite an existing file unless `--force` is passed.
|
|
65
|
+
*/
|
|
66
|
+
declare const agentsCommand: citty.CommandDef<{
|
|
67
|
+
force: {
|
|
68
|
+
type: "boolean";
|
|
69
|
+
description: string;
|
|
70
|
+
default: false;
|
|
71
|
+
};
|
|
72
|
+
framework: {
|
|
73
|
+
type: "string";
|
|
74
|
+
description: string;
|
|
75
|
+
};
|
|
76
|
+
}>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Canonical AGENTS.md generator.
|
|
80
|
+
*
|
|
81
|
+
* `kora agents-md` writes this file into an existing project so AI coding agents
|
|
82
|
+
* (and humans) build Kora apps correctly instead of drifting to fetch calls,
|
|
83
|
+
* hand-written types, and loading spinners. New projects scaffolded with
|
|
84
|
+
* `create-kora-app` already ship an AGENTS.md; this command covers the manual
|
|
85
|
+
* setup path and lets existing projects regenerate the guidance on demand.
|
|
86
|
+
*/
|
|
87
|
+
/** UI framework whose bindings section the generated AGENTS.md documents. */
|
|
88
|
+
type AgentsFramework = 'react' | 'vue' | 'svelte';
|
|
89
|
+
/** All supported frameworks, for validation and help text. */
|
|
90
|
+
declare const AGENTS_FRAMEWORKS: readonly AgentsFramework[];
|
|
91
|
+
/**
|
|
92
|
+
* Narrows an arbitrary string to a supported {@link AgentsFramework}.
|
|
93
|
+
*
|
|
94
|
+
* @param value - Candidate framework identifier
|
|
95
|
+
* @returns `true` when `value` is one of the supported frameworks
|
|
96
|
+
*/
|
|
97
|
+
declare function isAgentsFramework(value: string): value is AgentsFramework;
|
|
98
|
+
/**
|
|
99
|
+
* Detects the UI framework from a project's package manifest by looking for the
|
|
100
|
+
* matching `@korajs/*` binding package in dependencies or devDependencies.
|
|
101
|
+
*
|
|
102
|
+
* @param manifest - Parsed `package.json` contents
|
|
103
|
+
* @returns The detected framework, or `null` when no binding package is present
|
|
104
|
+
*/
|
|
105
|
+
declare function detectFrameworkFromManifest(manifest: unknown): AgentsFramework | null;
|
|
106
|
+
/**
|
|
107
|
+
* Renders the full AGENTS.md document for the given framework.
|
|
108
|
+
*
|
|
109
|
+
* The body (golden rules, data API cheat sheet, conflict handling, sync notes,
|
|
110
|
+
* commands) is framework-agnostic; only the bindings section changes. File paths
|
|
111
|
+
* are described as conventions so the guidance is correct for both scaffolded and
|
|
112
|
+
* manually configured projects.
|
|
113
|
+
*
|
|
114
|
+
* @param framework - UI framework whose bindings to document
|
|
115
|
+
* @returns The complete AGENTS.md file contents, terminated with a trailing newline
|
|
116
|
+
*/
|
|
117
|
+
declare function generateAgentsMd(framework: AgentsFramework): string;
|
|
118
|
+
|
|
62
119
|
/**
|
|
63
120
|
* Generates TypeScript interfaces from a SchemaDefinition.
|
|
64
121
|
* For each collection, produces three interfaces:
|
|
@@ -692,4 +749,4 @@ interface SyncProviderPresetOptions {
|
|
|
692
749
|
*/
|
|
693
750
|
declare function applySyncProviderPreset(options: SyncProviderPresetOptions): Promise<void>;
|
|
694
751
|
|
|
695
|
-
export { type AuthOption, type BuildArtifacts, ClackPromptClient, CliError, type ClientBuildOptions, type ClientBuildResult, type ContextAwareDeployAdapter, type CreateFlags, type CreatePreferences, DEPLOY_PLATFORMS, type DatabaseOption, type DatabaseProviderOption, type DeployAdapter, type DeployPlatform, type DeployResult, type DeployState, type DeployStateCreateInput, type DeployStatePatch, type DeploymentStatus, DevServerError, type DockerfileOptions, FlyAdapter, type FlyAdapterContext, type FlyAdapterOptions, type FlyCommandResult, type FlyCommandRunner, type FlyTomlOptions, type FrameworkOption, InvalidProjectError, type LogLine, type LogOptions, NodeFlyCommandRunner, NodeRailwayCommandRunner, PACKAGE_MANAGERS, type PackageManager, type PreferenceResolutionResult, PreferenceStore, type ProjectConfig, ProjectExistsError, type ProjectNameValidationResult, PromptCancelledError, type PromptClient, type ProvisionResult, RailwayAdapter, type RailwayAdapterContext, type RailwayAdapterOptions, type RailwayCommandResult, type RailwayCommandRunner, type RailwayJsonOptions, ReadlinePromptClient, SchemaNotFoundError, type SelectOption, type ServerBundleOptions, type ServerBundleResult, StubDeployAdapter, TEMPLATES, TEMPLATE_INFO, type TemplateContext, type TemplateInfo, type TemplateName, type TemplateSelectionInput, applySyncProviderPreset, buildClient, bundleServer, createDeployAdapter, createPromptClient, deployCommand, determineTemplateFromSelections, generateDeployPackageJson, generateDockerIgnore, generateDockerfile, generateFlyToml, generateRailwayJson, generateTypes, getCreatePreferencesOrDefault, getDefaultCreatePreferences, isAuthValue, isDatabaseProviderValue, isDatabaseValue, isDeployPlatform, isFrameworkValue, readDeployState, resetDeployState, resolveCreatePreferencesFlow, resolveDeployDirectory, resolveDeployStatePath, saveResolvedPreferences, shouldSavePreferences, updateDeployState, validateProjectName, writeDeployState, writeDockerIgnoreArtifact, writeDockerfileArtifact, writeFlyTomlArtifact, writeRailwayJsonArtifact };
|
|
752
|
+
export { AGENTS_FRAMEWORKS, type AgentsFramework, type AuthOption, type BuildArtifacts, ClackPromptClient, CliError, type ClientBuildOptions, type ClientBuildResult, type ContextAwareDeployAdapter, type CreateFlags, type CreatePreferences, DEPLOY_PLATFORMS, type DatabaseOption, type DatabaseProviderOption, type DeployAdapter, type DeployPlatform, type DeployResult, type DeployState, type DeployStateCreateInput, type DeployStatePatch, type DeploymentStatus, DevServerError, type DockerfileOptions, FlyAdapter, type FlyAdapterContext, type FlyAdapterOptions, type FlyCommandResult, type FlyCommandRunner, type FlyTomlOptions, type FrameworkOption, InvalidProjectError, type LogLine, type LogOptions, NodeFlyCommandRunner, NodeRailwayCommandRunner, PACKAGE_MANAGERS, type PackageManager, type PreferenceResolutionResult, PreferenceStore, type ProjectConfig, ProjectExistsError, type ProjectNameValidationResult, PromptCancelledError, type PromptClient, type ProvisionResult, RailwayAdapter, type RailwayAdapterContext, type RailwayAdapterOptions, type RailwayCommandResult, type RailwayCommandRunner, type RailwayJsonOptions, ReadlinePromptClient, SchemaNotFoundError, type SelectOption, type ServerBundleOptions, type ServerBundleResult, StubDeployAdapter, TEMPLATES, TEMPLATE_INFO, type TemplateContext, type TemplateInfo, type TemplateName, type TemplateSelectionInput, agentsCommand, applySyncProviderPreset, buildClient, bundleServer, createDeployAdapter, createPromptClient, deployCommand, detectFrameworkFromManifest, determineTemplateFromSelections, generateAgentsMd, generateDeployPackageJson, generateDockerIgnore, generateDockerfile, generateFlyToml, generateRailwayJson, generateTypes, getCreatePreferencesOrDefault, getDefaultCreatePreferences, isAgentsFramework, isAuthValue, isDatabaseProviderValue, isDatabaseValue, isDeployPlatform, isFrameworkValue, readDeployState, resetDeployState, resolveCreatePreferencesFlow, resolveDeployDirectory, resolveDeployStatePath, saveResolvedPreferences, shouldSavePreferences, updateDeployState, validateProjectName, writeDeployState, writeDockerIgnoreArtifact, writeDockerfileArtifact, writeFlyTomlArtifact, writeRailwayJsonArtifact };
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
AGENTS_FRAMEWORKS,
|
|
3
4
|
DEPLOY_PLATFORMS,
|
|
4
5
|
FlyAdapter,
|
|
5
6
|
NodeFlyCommandRunner,
|
|
6
7
|
NodeRailwayCommandRunner,
|
|
7
8
|
RailwayAdapter,
|
|
8
9
|
StubDeployAdapter,
|
|
10
|
+
agentsCommand,
|
|
9
11
|
buildClient,
|
|
10
12
|
bundleServer,
|
|
11
13
|
createDeployAdapter,
|
|
12
14
|
deployCommand,
|
|
15
|
+
detectFrameworkFromManifest,
|
|
16
|
+
generateAgentsMd,
|
|
13
17
|
generateDeployPackageJson,
|
|
14
18
|
generateDockerIgnore,
|
|
15
19
|
generateDockerfile,
|
|
16
20
|
generateFlyToml,
|
|
17
21
|
generateRailwayJson,
|
|
18
22
|
generateTypes,
|
|
23
|
+
isAgentsFramework,
|
|
19
24
|
isDeployPlatform,
|
|
20
25
|
readDeployState,
|
|
21
26
|
resetDeployState,
|
|
@@ -27,7 +32,7 @@ import {
|
|
|
27
32
|
writeDockerfileArtifact,
|
|
28
33
|
writeFlyTomlArtifact,
|
|
29
34
|
writeRailwayJsonArtifact
|
|
30
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-4IKCHRJ6.js";
|
|
31
36
|
import {
|
|
32
37
|
ClackPromptClient,
|
|
33
38
|
CliError,
|
|
@@ -54,9 +59,10 @@ import {
|
|
|
54
59
|
saveResolvedPreferences,
|
|
55
60
|
shouldSavePreferences,
|
|
56
61
|
validateProjectName
|
|
57
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-G2PBYMMA.js";
|
|
58
63
|
import "./chunk-5NI2FEQL.js";
|
|
59
64
|
export {
|
|
65
|
+
AGENTS_FRAMEWORKS,
|
|
60
66
|
ClackPromptClient,
|
|
61
67
|
CliError,
|
|
62
68
|
DEPLOY_PLATFORMS,
|
|
@@ -75,13 +81,16 @@ export {
|
|
|
75
81
|
StubDeployAdapter,
|
|
76
82
|
TEMPLATES,
|
|
77
83
|
TEMPLATE_INFO,
|
|
84
|
+
agentsCommand,
|
|
78
85
|
applySyncProviderPreset,
|
|
79
86
|
buildClient,
|
|
80
87
|
bundleServer,
|
|
81
88
|
createDeployAdapter,
|
|
82
89
|
createPromptClient,
|
|
83
90
|
deployCommand,
|
|
91
|
+
detectFrameworkFromManifest,
|
|
84
92
|
determineTemplateFromSelections,
|
|
93
|
+
generateAgentsMd,
|
|
85
94
|
generateDeployPackageJson,
|
|
86
95
|
generateDockerIgnore,
|
|
87
96
|
generateDockerfile,
|
|
@@ -90,6 +99,7 @@ export {
|
|
|
90
99
|
generateTypes,
|
|
91
100
|
getCreatePreferencesOrDefault,
|
|
92
101
|
getDefaultCreatePreferences,
|
|
102
|
+
isAgentsFramework,
|
|
93
103
|
isAuthValue,
|
|
94
104
|
isDatabaseProviderValue,
|
|
95
105
|
isDatabaseValue,
|
|
@@ -39,6 +39,7 @@ var SpectatorManager = class {
|
|
|
39
39
|
MergeEngine: (await import("@korajs/merge")).MergeEngine,
|
|
40
40
|
SyncEngine: (await import("@korajs/sync")).SyncEngine,
|
|
41
41
|
WebSocketTransport: (await import("@korajs/sync")).WebSocketTransport,
|
|
42
|
+
WebSocket: (await import("ws")).default,
|
|
42
43
|
testing: await import("korajs/testing"),
|
|
43
44
|
SimpleEventEmitter: (await import("@korajs/core/internal")).SimpleEventEmitter
|
|
44
45
|
};
|
|
@@ -57,7 +58,9 @@ var SpectatorManager = class {
|
|
|
57
58
|
const unsubscribeAudit = mods.testing.wireAuditPersistence(store, emitter);
|
|
58
59
|
const syncStore = new mods.testing.MergeAwareSyncStore(store, mergeEngine, emitter);
|
|
59
60
|
const engine = new mods.SyncEngine({
|
|
60
|
-
transport: new mods.WebSocketTransport(
|
|
61
|
+
transport: new mods.WebSocketTransport({
|
|
62
|
+
WebSocketImpl: mods.WebSocket
|
|
63
|
+
}),
|
|
61
64
|
store: syncStore,
|
|
62
65
|
queueStorage: new mods.testing.StoreQueueStorage(adapter),
|
|
63
66
|
syncState: new mods.testing.StoreSyncStatePersistence(store),
|
|
@@ -139,4 +142,4 @@ var SpectatorManager = class {
|
|
|
139
142
|
export {
|
|
140
143
|
SpectatorManager
|
|
141
144
|
};
|
|
142
|
-
//# sourceMappingURL=spectator-manager-
|
|
145
|
+
//# sourceMappingURL=spectator-manager-FTH2NFSE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/studio/spectator-manager.ts"],"sourcesContent":["import { mkdtempSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport type { SchemaDefinition } from '@korajs/core'\nimport type { WebSocketConstructor } from '@korajs/sync'\n\n/**\n * Kora Studio SPECTATOR: live, read-only inspection of a production sync\n * server.\n *\n * The spectator is a REAL Kora client — its own store, merge pipeline, and\n * sync engine — connected to the target server over the real WebSocket\n * protocol. It materializes its own replica into a throwaway database, which\n * every Studio view (records, per-field writers, op log, DAG, time travel,\n * merge audit) then reads. Nothing is simulated: what Studio shows is exactly\n * what any device syncing against that server computes.\n *\n * Read-only by construction: the spectator exposes NO mutation API, so it can\n * never push an operation to the server. It only receives, applies, and\n * observes. The server sees an ordinary client whose version vector starts\n * empty (so it receives the full history — which is what makes production\n * time travel possible) and which never sends operations.\n */\n\nexport interface SpectatorStatus {\n\turl: string\n\tconnected: boolean\n\toperationsReceived: number\n\tlastEventAt: number | null\n\tdbPath: string\n}\n\nexport interface SpectatorEvent {\n\tseq: number\n\tat: number\n\ttype: string\n\tsummary: string\n}\n\nexport interface SpectatorOptions {\n\turl: string\n\tschema: SchemaDefinition\n\t/** Bearer token forwarded through the sync auth handshake. */\n\ttoken?: string\n\t/** Client schema version override. Defaults to schema.version. */\n\tschemaVersion?: number\n}\n\n// NOTE: 'operation:applied' is in the event spec but not yet emitted by the\n// store — received-op counting uses 'sync:received' batches instead.\nconst FORWARDED_EVENTS = [\n\t'merge:started',\n\t'merge:conflict',\n\t'merge:completed',\n\t'sync:connected',\n\t'sync:disconnected',\n\t'sync:received',\n\t'sync:apply-failed',\n] as const\n\nconst MAX_EVENT_BUFFER = 500\n\n/** Structural types for the lazily-imported runtime packages. */\ninterface SpectatorRuntime {\n\tstore: {\n\t\topen(): Promise<void>\n\t\tclose(): Promise<void>\n\t\tsetLocalMutationHandler(handler: unknown): void\n\t}\n\tengine: { start(): Promise<void>; stop(): Promise<void> }\n\tunsubscribers: Array<() => void>\n}\n\nexport class SpectatorManager {\n\tprivate runtime: SpectatorRuntime | null = null\n\tprivate tmpDir: string | null = null\n\tprivate connected = false\n\tprivate operationsReceived = 0\n\tprivate lastEventAt: number | null = null\n\tprivate eventSeq = 0\n\tprivate readonly eventBuffer: SpectatorEvent[] = []\n\tprivate readonly eventListeners = new Set<(event: SpectatorEvent) => void>()\n\treadonly dbPath: string\n\n\tconstructor(private readonly options: SpectatorOptions) {\n\t\tthis.tmpDir = mkdtempSync(join(tmpdir(), 'kora-studio-spectator-'))\n\t\tthis.dbPath = join(this.tmpDir, 'spectator-replica.db')\n\t}\n\n\tasync start(): Promise<void> {\n\t\tlet mods: {\n\t\t\tStore: typeof import('@korajs/store').Store\n\t\t\tBetterSqlite3Adapter: typeof import('@korajs/store/better-sqlite3').BetterSqlite3Adapter\n\t\t\tMergeEngine: typeof import('@korajs/merge').MergeEngine\n\t\t\tSyncEngine: typeof import('@korajs/sync').SyncEngine\n\t\t\tWebSocketTransport: typeof import('@korajs/sync').WebSocketTransport\n\t\t\tWebSocket: typeof import('ws').default\n\t\t\ttesting: typeof import('korajs/testing')\n\t\t\tSimpleEventEmitter: typeof import('@korajs/core/internal').SimpleEventEmitter\n\t\t}\n\t\ttry {\n\t\t\tmods = {\n\t\t\t\tStore: (await import('@korajs/store')).Store,\n\t\t\t\tBetterSqlite3Adapter: (await import('@korajs/store/better-sqlite3')).BetterSqlite3Adapter,\n\t\t\t\tMergeEngine: (await import('@korajs/merge')).MergeEngine,\n\t\t\t\tSyncEngine: (await import('@korajs/sync')).SyncEngine,\n\t\t\t\tWebSocketTransport: (await import('@korajs/sync')).WebSocketTransport,\n\t\t\t\tWebSocket: (await import('ws')).default,\n\t\t\t\ttesting: await import('korajs/testing'),\n\t\t\t\tSimpleEventEmitter: (await import('@korajs/core/internal')).SimpleEventEmitter,\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Spectator mode needs the Kora runtime packages installed (pnpm add -D korajs @korajs/store @korajs/merge @korajs/sync better-sqlite3). Underlying error: ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t)\n\t\t}\n\n\t\tconst emitter = new mods.SimpleEventEmitter()\n\t\tconst adapter = new mods.BetterSqlite3Adapter(this.dbPath)\n\t\tconst store = new mods.Store({ schema: this.options.schema, adapter, emitter })\n\t\tawait store.open()\n\n\t\tconst mergeEngine = new mods.MergeEngine()\n\t\tconst pipeline = new mods.testing.ApplyPipeline({ store, mergeEngine, emitter })\n\t\tstore.setLocalMutationHandler(pipeline)\n\n\t\t// Production parity: merge decisions persist to the audit trail.\n\t\tconst unsubscribeAudit = mods.testing.wireAuditPersistence(store, emitter)\n\n\t\tconst syncStore = new mods.testing.MergeAwareSyncStore(store, mergeEngine, emitter)\n\t\tconst engine = new mods.SyncEngine({\n\t\t\ttransport: new mods.WebSocketTransport({\n\t\t\t\tWebSocketImpl: mods.WebSocket as unknown as WebSocketConstructor,\n\t\t\t}),\n\t\t\tstore: syncStore,\n\t\t\tqueueStorage: new mods.testing.StoreQueueStorage(adapter),\n\t\t\tsyncState: new mods.testing.StoreSyncStatePersistence(store),\n\t\t\tconfig: {\n\t\t\t\turl: this.options.url,\n\t\t\t\tschemaVersion: this.options.schemaVersion ?? this.options.schema.version,\n\t\t\t\t...(this.options.token\n\t\t\t\t\t? { auth: async () => ({ token: this.options.token as string }) }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\temitter,\n\t\t})\n\n\t\tconst unsubscribers = FORWARDED_EVENTS.map((type) =>\n\t\t\temitter.on(type, (event: Record<string, unknown>) => {\n\t\t\t\tthis.lastEventAt = Date.now()\n\t\t\t\tif (type === 'sync:connected') {\n\t\t\t\t\tthis.connected = true\n\t\t\t\t}\n\t\t\t\tif (type === 'sync:disconnected') {\n\t\t\t\t\tthis.connected = false\n\t\t\t\t}\n\t\t\t\tif (type === 'sync:received' && Array.isArray(event.operations)) {\n\t\t\t\t\tthis.operationsReceived += event.operations.length\n\t\t\t\t}\n\t\t\t\tthis.pushEvent(type, event)\n\t\t\t}),\n\t\t)\n\t\tunsubscribers.push(unsubscribeAudit)\n\n\t\tthis.runtime = { store, engine, unsubscribers }\n\t\tawait engine.start()\n\t}\n\n\tstatus(): SpectatorStatus {\n\t\treturn {\n\t\t\turl: this.options.url,\n\t\t\tconnected: this.connected,\n\t\t\toperationsReceived: this.operationsReceived,\n\t\t\tlastEventAt: this.lastEventAt,\n\t\t\tdbPath: this.dbPath,\n\t\t}\n\t}\n\n\trecentEvents(): SpectatorEvent[] {\n\t\treturn [...this.eventBuffer]\n\t}\n\n\tonEvent(listener: (event: SpectatorEvent) => void): () => void {\n\t\tthis.eventListeners.add(listener)\n\t\treturn () => this.eventListeners.delete(listener)\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.runtime) {\n\t\t\tfor (const unsub of this.runtime.unsubscribers) {\n\t\t\t\tunsub()\n\t\t\t}\n\t\t\tawait this.runtime.engine.stop().catch(() => {})\n\t\t\tawait this.runtime.store.close().catch(() => {})\n\t\t\tthis.runtime = null\n\t\t}\n\t\tif (this.tmpDir) {\n\t\t\trmSync(this.tmpDir, { recursive: true, force: true })\n\t\t\tthis.tmpDir = null\n\t\t}\n\t}\n\n\tprivate pushEvent(type: string, payload: Record<string, unknown>): void {\n\t\tconst summary =\n\t\t\ttype === 'sync:received' && Array.isArray(payload.operations)\n\t\t\t\t? `received ${payload.operations.length} op(s)`\n\t\t\t\t: type.replace('sync:', '').replace('merge:', 'merge ')\n\t\tconst event: SpectatorEvent = {\n\t\t\tseq: ++this.eventSeq,\n\t\t\tat: Date.now(),\n\t\t\ttype,\n\t\t\tsummary,\n\t\t}\n\t\tthis.eventBuffer.push(event)\n\t\tif (this.eventBuffer.length > MAX_EVENT_BUFFER) {\n\t\t\tthis.eventBuffer.shift()\n\t\t}\n\t\tfor (const listener of this.eventListeners) {\n\t\t\tlistener(event)\n\t\t}\n\t}\n}\n"],"mappings":";;;AAAA,SAAS,aAAa,cAAc;AACpC,SAAS,cAAc;AACvB,SAAS,YAAY;AAgDrB,IAAM,mBAAmB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAEA,IAAM,mBAAmB;AAalB,IAAM,mBAAN,MAAuB;AAAA,EAW7B,YAA6B,SAA2B;AAA3B;AAC5B,SAAK,SAAS,YAAY,KAAK,OAAO,GAAG,wBAAwB,CAAC;AAClE,SAAK,SAAS,KAAK,KAAK,QAAQ,sBAAsB;AAAA,EACvD;AAAA,EAH6B;AAAA,EAVrB,UAAmC;AAAA,EACnC,SAAwB;AAAA,EACxB,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,cAA6B;AAAA,EAC7B,WAAW;AAAA,EACF,cAAgC,CAAC;AAAA,EACjC,iBAAiB,oBAAI,IAAqC;AAAA,EAClE;AAAA,EAOT,MAAM,QAAuB;AAC5B,QAAI;AAUJ,QAAI;AACH,aAAO;AAAA,QACN,QAAQ,MAAM,OAAO,eAAe,GAAG;AAAA,QACvC,uBAAuB,MAAM,OAAO,8BAA8B,GAAG;AAAA,QACrE,cAAc,MAAM,OAAO,eAAe,GAAG;AAAA,QAC7C,aAAa,MAAM,OAAO,cAAc,GAAG;AAAA,QAC3C,qBAAqB,MAAM,OAAO,cAAc,GAAG;AAAA,QACnD,YAAY,MAAM,OAAO,IAAI,GAAG;AAAA,QAChC,SAAS,MAAM,OAAO,gBAAgB;AAAA,QACtC,qBAAqB,MAAM,OAAO,uBAAuB,GAAG;AAAA,MAC7D;AAAA,IACD,SAAS,OAAO;AACf,YAAM,IAAI;AAAA,QACT,4JACC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CACtD;AAAA,MACD;AAAA,IACD;AAEA,UAAM,UAAU,IAAI,KAAK,mBAAmB;AAC5C,UAAM,UAAU,IAAI,KAAK,qBAAqB,KAAK,MAAM;AACzD,UAAM,QAAQ,IAAI,KAAK,MAAM,EAAE,QAAQ,KAAK,QAAQ,QAAQ,SAAS,QAAQ,CAAC;AAC9E,UAAM,MAAM,KAAK;AAEjB,UAAM,cAAc,IAAI,KAAK,YAAY;AACzC,UAAM,WAAW,IAAI,KAAK,QAAQ,cAAc,EAAE,OAAO,aAAa,QAAQ,CAAC;AAC/E,UAAM,wBAAwB,QAAQ;AAGtC,UAAM,mBAAmB,KAAK,QAAQ,qBAAqB,OAAO,OAAO;AAEzE,UAAM,YAAY,IAAI,KAAK,QAAQ,oBAAoB,OAAO,aAAa,OAAO;AAClF,UAAM,SAAS,IAAI,KAAK,WAAW;AAAA,MAClC,WAAW,IAAI,KAAK,mBAAmB;AAAA,QACtC,eAAe,KAAK;AAAA,MACrB,CAAC;AAAA,MACD,OAAO;AAAA,MACP,cAAc,IAAI,KAAK,QAAQ,kBAAkB,OAAO;AAAA,MACxD,WAAW,IAAI,KAAK,QAAQ,0BAA0B,KAAK;AAAA,MAC3D,QAAQ;AAAA,QACP,KAAK,KAAK,QAAQ;AAAA,QAClB,eAAe,KAAK,QAAQ,iBAAiB,KAAK,QAAQ,OAAO;AAAA,QACjE,GAAI,KAAK,QAAQ,QACd,EAAE,MAAM,aAAa,EAAE,OAAO,KAAK,QAAQ,MAAgB,GAAG,IAC9D,CAAC;AAAA,MACL;AAAA,MACA;AAAA,IACD,CAAC;AAED,UAAM,gBAAgB,iBAAiB;AAAA,MAAI,CAAC,SAC3C,QAAQ,GAAG,MAAM,CAAC,UAAmC;AACpD,aAAK,cAAc,KAAK,IAAI;AAC5B,YAAI,SAAS,kBAAkB;AAC9B,eAAK,YAAY;AAAA,QAClB;AACA,YAAI,SAAS,qBAAqB;AACjC,eAAK,YAAY;AAAA,QAClB;AACA,YAAI,SAAS,mBAAmB,MAAM,QAAQ,MAAM,UAAU,GAAG;AAChE,eAAK,sBAAsB,MAAM,WAAW;AAAA,QAC7C;AACA,aAAK,UAAU,MAAM,KAAK;AAAA,MAC3B,CAAC;AAAA,IACF;AACA,kBAAc,KAAK,gBAAgB;AAEnC,SAAK,UAAU,EAAE,OAAO,QAAQ,cAAc;AAC9C,UAAM,OAAO,MAAM;AAAA,EACpB;AAAA,EAEA,SAA0B;AACzB,WAAO;AAAA,MACN,KAAK,KAAK,QAAQ;AAAA,MAClB,WAAW,KAAK;AAAA,MAChB,oBAAoB,KAAK;AAAA,MACzB,aAAa,KAAK;AAAA,MAClB,QAAQ,KAAK;AAAA,IACd;AAAA,EACD;AAAA,EAEA,eAAiC;AAChC,WAAO,CAAC,GAAG,KAAK,WAAW;AAAA,EAC5B;AAAA,EAEA,QAAQ,UAAuD;AAC9D,SAAK,eAAe,IAAI,QAAQ;AAChC,WAAO,MAAM,KAAK,eAAe,OAAO,QAAQ;AAAA,EACjD;AAAA,EAEA,MAAM,QAAuB;AAC5B,QAAI,KAAK,SAAS;AACjB,iBAAW,SAAS,KAAK,QAAQ,eAAe;AAC/C,cAAM;AAAA,MACP;AACA,YAAM,KAAK,QAAQ,OAAO,KAAK,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAC/C,YAAM,KAAK,QAAQ,MAAM,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAC/C,WAAK,UAAU;AAAA,IAChB;AACA,QAAI,KAAK,QAAQ;AAChB,aAAO,KAAK,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACpD,WAAK,SAAS;AAAA,IACf;AAAA,EACD;AAAA,EAEQ,UAAU,MAAc,SAAwC;AACvE,UAAM,UACL,SAAS,mBAAmB,MAAM,QAAQ,QAAQ,UAAU,IACzD,YAAY,QAAQ,WAAW,MAAM,WACrC,KAAK,QAAQ,SAAS,EAAE,EAAE,QAAQ,UAAU,QAAQ;AACxD,UAAM,QAAwB;AAAA,MAC7B,KAAK,EAAE,KAAK;AAAA,MACZ,IAAI,KAAK,IAAI;AAAA,MACb;AAAA,MACA;AAAA,IACD;AACA,SAAK,YAAY,KAAK,KAAK;AAC3B,QAAI,KAAK,YAAY,SAAS,kBAAkB;AAC/C,WAAK,YAAY,MAAM;AAAA,IACxB;AACA,eAAW,YAAY,KAAK,gBAAgB;AAC3C,eAAS,KAAK;AAAA,IACf;AAAA,EACD;AACD;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korajs/cli",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.11",
|
|
4
4
|
"description": "Kora.js CLI tooling and project scaffolding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -37,22 +37,24 @@
|
|
|
37
37
|
"conf": "^15.1.0",
|
|
38
38
|
"esbuild": "^0.28.0",
|
|
39
39
|
"validate-npm-package-name": "^7.0.2",
|
|
40
|
-
"
|
|
40
|
+
"ws": "^8.18.0",
|
|
41
|
+
"@korajs/core": "1.0.0-beta.11"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@types/better-sqlite3": "^7.6.13",
|
|
44
45
|
"@types/node": "^25.5.2",
|
|
45
46
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
47
|
+
"@types/ws": "^8.5.13",
|
|
46
48
|
"better-sqlite3": "^12.8.0",
|
|
47
49
|
"tsup": "^8.3.6",
|
|
48
50
|
"typescript": "^5.7.3",
|
|
49
51
|
"vitest": "^3.0.4",
|
|
50
|
-
"@korajs/
|
|
51
|
-
"@korajs/
|
|
52
|
-
"@korajs/
|
|
53
|
-
"korajs": "1.0.0-beta.
|
|
54
|
-
"@korajs/
|
|
55
|
-
"
|
|
52
|
+
"@korajs/server": "1.0.0-beta.11",
|
|
53
|
+
"@korajs/sync": "1.0.0-beta.11",
|
|
54
|
+
"@korajs/store": "1.0.0-beta.11",
|
|
55
|
+
"@korajs/test": "1.0.0-beta.11",
|
|
56
|
+
"@korajs/merge": "1.0.0-beta.11",
|
|
57
|
+
"korajs": "1.0.0-beta.11"
|
|
56
58
|
},
|
|
57
59
|
"license": "MIT",
|
|
58
60
|
"scripts": {
|
|
@@ -47,7 +47,7 @@ To enable real-time sync across devices, update `src/main.tsx`:
|
|
|
47
47
|
const app = createApp({
|
|
48
48
|
schema,
|
|
49
49
|
sync: {
|
|
50
|
-
url: import.meta.env.VITE_SYNC_URL || 'ws://localhost:3001',
|
|
50
|
+
url: import.meta.env.VITE_SYNC_URL || 'ws://localhost:3001/kora-sync',
|
|
51
51
|
},
|
|
52
52
|
// ...
|
|
53
53
|
})
|
|
@@ -6,23 +6,26 @@ import { defineConfig } from 'vite'
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Adds Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* enabling SharedArrayBuffer where browsers support it while keeping common
|
|
10
|
+
* third-party embeds such as YouTube and Vimeo usable. Kora falls back to
|
|
11
|
+
* durable IndexedDB storage when SharedArrayBuffer is unavailable.
|
|
11
12
|
*/
|
|
12
13
|
function crossOriginIsolation(): Plugin {
|
|
14
|
+
const embedderPolicy = process.env.KORA_COEP_POLICY || 'credentialless'
|
|
15
|
+
|
|
13
16
|
return {
|
|
14
17
|
name: 'cross-origin-isolation',
|
|
15
18
|
configureServer(server) {
|
|
16
19
|
server.middlewares.use((_req, res, next) => {
|
|
17
20
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
18
|
-
res.setHeader('Cross-Origin-Embedder-Policy',
|
|
21
|
+
res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
|
|
19
22
|
next()
|
|
20
23
|
})
|
|
21
24
|
},
|
|
22
25
|
configurePreviewServer(server) {
|
|
23
26
|
server.middlewares.use((_req, res, next) => {
|
|
24
27
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
25
|
-
res.setHeader('Cross-Origin-Embedder-Policy',
|
|
28
|
+
res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
|
|
26
29
|
next()
|
|
27
30
|
})
|
|
28
31
|
},
|
|
@@ -11,7 +11,7 @@ npm run dev
|
|
|
11
11
|
This starts both the Vite dev server and the Kora sync server.
|
|
12
12
|
|
|
13
13
|
- **App**: http://localhost:5173
|
|
14
|
-
- **Sync server**: ws://localhost:3001
|
|
14
|
+
- **Sync server**: ws://localhost:3001/kora-sync
|
|
15
15
|
|
|
16
16
|
## Project Structure
|
|
17
17
|
|
|
@@ -40,11 +40,12 @@ writes, and `useTodos.ts` is the React binding that exposes todo data and action
|
|
|
40
40
|
|
|
41
41
|
| Variable | Default | Description |
|
|
42
42
|
|----------|---------|-------------|
|
|
43
|
-
| `VITE_SYNC_URL` | `ws://localhost:3001` | WebSocket URL for the sync
|
|
43
|
+
| `VITE_SYNC_URL` | `ws://localhost:3001/kora-sync` | WebSocket URL for the sync endpoint |
|
|
44
44
|
| `VITE_AUTH_URL` | current origin | HTTP URL for `/auth/*` routes |
|
|
45
45
|
| `PORT` | `3001` | Sync server port |
|
|
46
|
-
| `KORA_SERVER_DB` |
|
|
47
|
-
| `KORA_AUTH_DB` |
|
|
46
|
+
| `KORA_SERVER_DB` | `./.kora/kora-server.db` | SQLite file used by the sync server when `DATABASE_URL` is empty |
|
|
47
|
+
| `KORA_AUTH_DB` | `./.kora/kora-auth.db` | SQLite file used by OAuth state and linked identities when `DATABASE_URL` is empty |
|
|
48
|
+
| `KORA_COEP_POLICY` | `credentialless` | Dev/preview Cross-Origin-Embedder-Policy. Use `require-corp` only when every embedded resource opts in. |
|
|
48
49
|
| `DATABASE_URL` | (none) | PostgreSQL connection string for production sync server deployments |
|
|
49
50
|
| `KORA_SYNC_PATH` | `/kora-sync` | WebSocket path exposed by the sync server |
|
|
50
51
|
| `KORA_AUTH_SECRET` | (none) | Enables built-in `/auth/*` routes and authenticated sync when set |
|
|
@@ -94,7 +95,7 @@ The client and sync server must be deployed separately:
|
|
|
94
95
|
Set `VITE_SYNC_URL` to your deployed sync server URL when building:
|
|
95
96
|
|
|
96
97
|
```bash
|
|
97
|
-
VITE_SYNC_URL=wss://your-server.example.com npm run build
|
|
98
|
+
VITE_SYNC_URL=wss://your-server.example.com/kora-sync npm run build
|
|
98
99
|
```
|
|
99
100
|
|
|
100
101
|
## Learn More
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
1
2
|
import {
|
|
2
3
|
type CreateKoraAuthServerOptions,
|
|
3
4
|
createKoraAuthServer,
|
|
@@ -12,6 +13,45 @@ import {
|
|
|
12
13
|
} from '@korajs/server'
|
|
13
14
|
import schema from './src/schema'
|
|
14
15
|
|
|
16
|
+
loadLocalEnv()
|
|
17
|
+
|
|
18
|
+
function loadLocalEnv(mode = process.env.NODE_ENV || 'development') {
|
|
19
|
+
const existingKeys = new Set(Object.keys(process.env))
|
|
20
|
+
for (const file of ['.env', '.env.local', `.env.${mode}`, `.env.${mode}.local`]) {
|
|
21
|
+
if (!existsSync(file)) continue
|
|
22
|
+
for (const [key, value] of Object.entries(parseEnvFile(readFileSync(file, 'utf8')))) {
|
|
23
|
+
if (!existingKeys.has(key)) {
|
|
24
|
+
process.env[key] = value
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseEnvFile(contents: string): Record<string, string> {
|
|
31
|
+
const env: Record<string, string> = {}
|
|
32
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
33
|
+
const line = rawLine.trim()
|
|
34
|
+
if (!line || line.startsWith('#')) continue
|
|
35
|
+
const normalized = line.startsWith('export ') ? line.slice('export '.length).trim() : line
|
|
36
|
+
const equalsIndex = normalized.indexOf('=')
|
|
37
|
+
if (equalsIndex <= 0) continue
|
|
38
|
+
const key = normalized.slice(0, equalsIndex).trim()
|
|
39
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue
|
|
40
|
+
env[key] = parseEnvValue(normalized.slice(equalsIndex + 1).trim())
|
|
41
|
+
}
|
|
42
|
+
return env
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseEnvValue(value: string): string {
|
|
46
|
+
const quote = value[0]
|
|
47
|
+
if ((quote === '"' || quote === "'") && value[value.length - 1] === quote) {
|
|
48
|
+
const inner = value.slice(1, -1)
|
|
49
|
+
return quote === '"' ? inner.replace(/\\n/g, '\n').replace(/\\r/g, '\r') : inner
|
|
50
|
+
}
|
|
51
|
+
const commentIndex = value.search(/\s#/)
|
|
52
|
+
return (commentIndex >= 0 ? value.slice(0, commentIndex) : value).trim()
|
|
53
|
+
}
|
|
54
|
+
|
|
15
55
|
async function createStore() {
|
|
16
56
|
if (process.env.DATABASE_URL) {
|
|
17
57
|
return createPostgresServerStore({
|
|
@@ -20,7 +60,7 @@ async function createStore() {
|
|
|
20
60
|
}
|
|
21
61
|
|
|
22
62
|
return createSqliteServerStore({
|
|
23
|
-
filename: process.env.KORA_SERVER_DB || '
|
|
63
|
+
filename: process.env.KORA_SERVER_DB || './.kora/kora-server.db',
|
|
24
64
|
})
|
|
25
65
|
}
|
|
26
66
|
|
|
@@ -36,7 +76,10 @@ async function start() {
|
|
|
36
76
|
staticDir: './dist',
|
|
37
77
|
syncPath,
|
|
38
78
|
httpRoutes: auth ? [{ path: '/auth', handle: auth.handleRequest }] : undefined,
|
|
39
|
-
syncOptions:
|
|
79
|
+
syncOptions: {
|
|
80
|
+
schemaVersion: schema.version,
|
|
81
|
+
...(auth ? { auth: auth.auth } : {}),
|
|
82
|
+
},
|
|
40
83
|
operationalAuth: {
|
|
41
84
|
adminToken: process.env.KORA_ADMIN_TOKEN,
|
|
42
85
|
metricsToken: process.env.KORA_METRICS_TOKEN,
|
|
@@ -72,7 +115,7 @@ async function createOAuthConfig(): Promise<CreateKoraAuthServerOptions['oauth']
|
|
|
72
115
|
const oauthStores = process.env.DATABASE_URL
|
|
73
116
|
? await createPostgresOAuthStores({ connectionString: process.env.DATABASE_URL })
|
|
74
117
|
: await createSqliteOAuthStores({
|
|
75
|
-
filename: process.env.KORA_AUTH_DB || '
|
|
118
|
+
filename: process.env.KORA_AUTH_DB || './.kora/kora-auth.db',
|
|
76
119
|
})
|
|
77
120
|
|
|
78
121
|
return {
|
|
@@ -6,23 +6,26 @@ import { defineConfig } from 'vite'
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Adds Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* enabling SharedArrayBuffer where browsers support it while keeping common
|
|
10
|
+
* third-party embeds such as YouTube and Vimeo usable. Kora falls back to
|
|
11
|
+
* durable IndexedDB storage when SharedArrayBuffer is unavailable.
|
|
11
12
|
*/
|
|
12
13
|
function crossOriginIsolation(): Plugin {
|
|
14
|
+
const embedderPolicy = process.env.KORA_COEP_POLICY || 'credentialless'
|
|
15
|
+
|
|
13
16
|
return {
|
|
14
17
|
name: 'cross-origin-isolation',
|
|
15
18
|
configureServer(server) {
|
|
16
19
|
server.middlewares.use((_req, res, next) => {
|
|
17
20
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
18
|
-
res.setHeader('Cross-Origin-Embedder-Policy',
|
|
21
|
+
res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
|
|
19
22
|
next()
|
|
20
23
|
})
|
|
21
24
|
},
|
|
22
25
|
configurePreviewServer(server) {
|
|
23
26
|
server.middlewares.use((_req, res, next) => {
|
|
24
27
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
25
|
-
res.setHeader('Cross-Origin-Embedder-Policy',
|
|
28
|
+
res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
|
|
26
29
|
next()
|
|
27
30
|
})
|
|
28
31
|
},
|
|
@@ -81,12 +84,19 @@ export default defineConfig({
|
|
|
81
84
|
},
|
|
82
85
|
server: {
|
|
83
86
|
allowedHosts: true,
|
|
87
|
+
watch: {
|
|
88
|
+
ignored: ['**/.kora/**', '**/kora/generated/**', '**/*.db', '**/*.db-shm', '**/*.db-wal'],
|
|
89
|
+
},
|
|
84
90
|
proxy: {
|
|
85
91
|
'/kora-sync': {
|
|
86
92
|
target: 'ws://localhost:3001',
|
|
87
93
|
ws: true,
|
|
88
94
|
rewriteWsOrigin: true,
|
|
89
95
|
},
|
|
96
|
+
'/auth': {
|
|
97
|
+
target: 'http://localhost:3001',
|
|
98
|
+
changeOrigin: true,
|
|
99
|
+
},
|
|
90
100
|
},
|
|
91
101
|
},
|
|
92
102
|
})
|
|
@@ -47,7 +47,7 @@ To enable real-time sync across devices, update `src/main.tsx`:
|
|
|
47
47
|
const app = createApp({
|
|
48
48
|
schema,
|
|
49
49
|
sync: {
|
|
50
|
-
url: import.meta.env.VITE_SYNC_URL || 'ws://localhost:3001',
|
|
50
|
+
url: import.meta.env.VITE_SYNC_URL || 'ws://localhost:3001/kora-sync',
|
|
51
51
|
},
|
|
52
52
|
// ...
|
|
53
53
|
})
|
|
@@ -7,23 +7,26 @@ import { defineConfig } from 'vite'
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Adds Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* enabling SharedArrayBuffer where browsers support it while keeping common
|
|
11
|
+
* third-party embeds such as YouTube and Vimeo usable. Kora falls back to
|
|
12
|
+
* durable IndexedDB storage when SharedArrayBuffer is unavailable.
|
|
12
13
|
*/
|
|
13
14
|
function crossOriginIsolation(): Plugin {
|
|
15
|
+
const embedderPolicy = process.env.KORA_COEP_POLICY || 'credentialless'
|
|
16
|
+
|
|
14
17
|
return {
|
|
15
18
|
name: 'cross-origin-isolation',
|
|
16
19
|
configureServer(server) {
|
|
17
20
|
server.middlewares.use((_req, res, next) => {
|
|
18
21
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
19
|
-
res.setHeader('Cross-Origin-Embedder-Policy',
|
|
22
|
+
res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
|
|
20
23
|
next()
|
|
21
24
|
})
|
|
22
25
|
},
|
|
23
26
|
configurePreviewServer(server) {
|
|
24
27
|
server.middlewares.use((_req, res, next) => {
|
|
25
28
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
|
26
|
-
res.setHeader('Cross-Origin-Embedder-Policy',
|
|
29
|
+
res.setHeader('Cross-Origin-Embedder-Policy', embedderPolicy)
|
|
27
30
|
next()
|
|
28
31
|
})
|
|
29
32
|
},
|