create-eventus-app 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-5JPHU7SZ.js +108 -0
- package/dist/chunk-BFKUHPSY.js +58 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +121 -0
- package/dist/scaffold.d.ts +17 -0
- package/dist/scaffold.js +6 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.js +20 -0
- package/package.json +40 -0
- package/template/README.md +22 -0
- package/template/eventus.manifest.json +6 -0
- package/template/index.html +12 -0
- package/template/package.json +24 -0
- package/template/src/App.tsx +122 -0
- package/template/src/eventus-env.d.ts +15 -0
- package/template/src/main.tsx +11 -0
- package/template/src/styles.css +114 -0
- package/template/tsconfig.json +20 -0
- package/template/vite.config.ts +7 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// src/scaffold.ts
|
|
2
|
+
import {
|
|
3
|
+
cp,
|
|
4
|
+
mkdir,
|
|
5
|
+
readdir,
|
|
6
|
+
readFile,
|
|
7
|
+
rm,
|
|
8
|
+
stat,
|
|
9
|
+
writeFile
|
|
10
|
+
} from "fs/promises";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
var DEFAULT_TEMPLATE_CANDIDATES = [
|
|
14
|
+
path.resolve(
|
|
15
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
16
|
+
"..",
|
|
17
|
+
"template"
|
|
18
|
+
),
|
|
19
|
+
path.resolve(
|
|
20
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
21
|
+
"..",
|
|
22
|
+
"..",
|
|
23
|
+
"template-react",
|
|
24
|
+
"template"
|
|
25
|
+
)
|
|
26
|
+
];
|
|
27
|
+
var PRESERVED_DIRECTORY_ENTRIES = /* @__PURE__ */ new Set([".git"]);
|
|
28
|
+
async function scaffoldProject(options) {
|
|
29
|
+
const templateDir = await resolveTemplateDir(options.templateDir);
|
|
30
|
+
const isTargetDirectoryNonEmpty = await directoryHasFiles(options.targetDir);
|
|
31
|
+
if (isTargetDirectoryNonEmpty && !options.force) {
|
|
32
|
+
const shouldContinue = options.onConfirmOverwrite ? await options.onConfirmOverwrite() : false;
|
|
33
|
+
if (!shouldContinue) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Target directory ${options.targetDir} is not empty. Use --force to overwrite it.`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
await mkdir(options.targetDir, { recursive: true });
|
|
40
|
+
if (isTargetDirectoryNonEmpty) {
|
|
41
|
+
await clearTargetDirectory(options.targetDir);
|
|
42
|
+
}
|
|
43
|
+
await cp(templateDir, options.targetDir, { recursive: true });
|
|
44
|
+
const replacements = /* @__PURE__ */ new Map([
|
|
45
|
+
["__EVENTUS_PACKAGE_NAME__", options.packageName],
|
|
46
|
+
["__EVENTUS_DISPLAY_NAME__", options.displayName],
|
|
47
|
+
["__EVENTUS_APP_ID__", options.appId],
|
|
48
|
+
["__EVENTUS_SDK_VERSION__", options.sdkVersion],
|
|
49
|
+
["__EVENTUS_VITE_PLUGIN_VERSION__", options.vitePluginVersion]
|
|
50
|
+
]);
|
|
51
|
+
await replacePlaceholders(options.targetDir, replacements);
|
|
52
|
+
}
|
|
53
|
+
async function resolveTemplateDir(explicitTemplateDir) {
|
|
54
|
+
if (explicitTemplateDir) {
|
|
55
|
+
return explicitTemplateDir;
|
|
56
|
+
}
|
|
57
|
+
for (const candidate of DEFAULT_TEMPLATE_CANDIDATES) {
|
|
58
|
+
try {
|
|
59
|
+
const candidateStats = await stat(candidate);
|
|
60
|
+
if (candidateStats.isDirectory()) {
|
|
61
|
+
return candidate;
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
throw new Error("Unable to locate the Eventus React template.");
|
|
67
|
+
}
|
|
68
|
+
async function directoryHasFiles(targetDir) {
|
|
69
|
+
try {
|
|
70
|
+
const entries = await readdir(targetDir);
|
|
71
|
+
return entries.length > 0;
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function clearTargetDirectory(targetDir) {
|
|
77
|
+
const entries = await readdir(targetDir, { withFileTypes: true });
|
|
78
|
+
await Promise.all(
|
|
79
|
+
entries.filter((entry) => !PRESERVED_DIRECTORY_ENTRIES.has(entry.name)).map(
|
|
80
|
+
(entry) => rm(path.join(targetDir, entry.name), {
|
|
81
|
+
recursive: true,
|
|
82
|
+
force: true
|
|
83
|
+
})
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
async function replacePlaceholders(directory, replacements) {
|
|
88
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const fullPath = path.join(directory, entry.name);
|
|
91
|
+
if (entry.isDirectory()) {
|
|
92
|
+
await replacePlaceholders(fullPath, replacements);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const fileContents = await readFile(fullPath, "utf8");
|
|
96
|
+
let nextContents = fileContents;
|
|
97
|
+
for (const [placeholder, value] of replacements) {
|
|
98
|
+
nextContents = nextContents.replaceAll(placeholder, value);
|
|
99
|
+
}
|
|
100
|
+
if (nextContents !== fileContents) {
|
|
101
|
+
await writeFile(fullPath, nextContents, "utf8");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export {
|
|
107
|
+
scaffoldProject
|
|
108
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/utils.ts
|
|
2
|
+
import { readFile } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import validateNpmPackageName from "validate-npm-package-name";
|
|
5
|
+
var PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
|
|
6
|
+
function resolveProjectInput(input, cwd = process.cwd()) {
|
|
7
|
+
const trimmedInput = input.trim();
|
|
8
|
+
const targetDir = path.resolve(cwd, trimmedInput);
|
|
9
|
+
const directoryName = path.basename(targetDir);
|
|
10
|
+
return {
|
|
11
|
+
input: trimmedInput,
|
|
12
|
+
targetDir,
|
|
13
|
+
directoryName,
|
|
14
|
+
packageName: directoryName
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function validateProjectName(name) {
|
|
18
|
+
const validation = validateNpmPackageName(name);
|
|
19
|
+
if (validation.validForNewPackages) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return [...validation.errors ?? [], ...validation.warnings ?? []];
|
|
23
|
+
}
|
|
24
|
+
function toDisplayName(projectName) {
|
|
25
|
+
return projectName.split(/[-_.\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
|
|
26
|
+
}
|
|
27
|
+
function toAppId(projectName) {
|
|
28
|
+
return projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
|
|
29
|
+
}
|
|
30
|
+
function isPackageManager(value) {
|
|
31
|
+
return PACKAGE_MANAGERS.includes(value);
|
|
32
|
+
}
|
|
33
|
+
async function readToolkitVersion(packageFileUrl) {
|
|
34
|
+
const packageJson = JSON.parse(await readFile(packageFileUrl, "utf8"));
|
|
35
|
+
return packageJson.version ?? "0.1.0";
|
|
36
|
+
}
|
|
37
|
+
function createNextStepsMessage(packageManager, projectDirectory) {
|
|
38
|
+
const relativeDirectory = path.relative(process.cwd(), projectDirectory) || ".";
|
|
39
|
+
const installCommand = packageManager === "npm" ? "npm install" : `${packageManager} install`;
|
|
40
|
+
const devCommand = packageManager === "npm" ? "npm run dev" : `${packageManager} dev`;
|
|
41
|
+
return [
|
|
42
|
+
"Next steps:",
|
|
43
|
+
` cd ${relativeDirectory}`,
|
|
44
|
+
` ${installCommand}`,
|
|
45
|
+
` ${devCommand}`
|
|
46
|
+
].join("\n");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
PACKAGE_MANAGERS,
|
|
51
|
+
resolveProjectInput,
|
|
52
|
+
validateProjectName,
|
|
53
|
+
toDisplayName,
|
|
54
|
+
toAppId,
|
|
55
|
+
isPackageManager,
|
|
56
|
+
readToolkitVersion,
|
|
57
|
+
createNextStepsMessage
|
|
58
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
scaffoldProject
|
|
4
|
+
} from "./chunk-5JPHU7SZ.js";
|
|
5
|
+
import {
|
|
6
|
+
PACKAGE_MANAGERS,
|
|
7
|
+
createNextStepsMessage,
|
|
8
|
+
isPackageManager,
|
|
9
|
+
readToolkitVersion,
|
|
10
|
+
resolveProjectInput,
|
|
11
|
+
toAppId,
|
|
12
|
+
toDisplayName,
|
|
13
|
+
validateProjectName
|
|
14
|
+
} from "./chunk-BFKUHPSY.js";
|
|
15
|
+
|
|
16
|
+
// src/index.ts
|
|
17
|
+
import {
|
|
18
|
+
cancel,
|
|
19
|
+
confirm,
|
|
20
|
+
intro,
|
|
21
|
+
isCancel,
|
|
22
|
+
outro,
|
|
23
|
+
select,
|
|
24
|
+
text
|
|
25
|
+
} from "@clack/prompts";
|
|
26
|
+
import { cac } from "cac";
|
|
27
|
+
async function run() {
|
|
28
|
+
const packageVersion = await readToolkitVersion(new URL("../package.json", import.meta.url));
|
|
29
|
+
const cli = cac("create-eventus-app");
|
|
30
|
+
cli.command("[targetDir]", "Create a new Eventus mini app").option("--package-manager <name>", "Package manager to show in next steps").option("--force", "Allow scaffolding into a non-empty directory").option("--sdk-version <version>", "Internal override for local SDK testing").option(
|
|
31
|
+
"--vite-plugin-version <version>",
|
|
32
|
+
"Internal override for local Vite plugin testing"
|
|
33
|
+
).action(async (targetDir, options) => {
|
|
34
|
+
intro("Create Eventus App");
|
|
35
|
+
try {
|
|
36
|
+
const resolvedInput = await promptForProject(targetDir);
|
|
37
|
+
const packageManager = await resolvePackageManager(options.packageManager);
|
|
38
|
+
const toolkitVersion = options.sdkVersion && options.vitePluginVersion ? void 0 : packageVersion;
|
|
39
|
+
const sdkVersion = options.sdkVersion?.trim() || toolkitVersion || packageVersion;
|
|
40
|
+
const vitePluginVersion = options.vitePluginVersion?.trim() || toolkitVersion || packageVersion;
|
|
41
|
+
await scaffoldProject({
|
|
42
|
+
targetDir: resolvedInput.targetDir,
|
|
43
|
+
packageName: resolvedInput.packageName,
|
|
44
|
+
displayName: toDisplayName(resolvedInput.packageName),
|
|
45
|
+
appId: toAppId(resolvedInput.packageName),
|
|
46
|
+
packageManager,
|
|
47
|
+
sdkVersion,
|
|
48
|
+
vitePluginVersion,
|
|
49
|
+
force: options.force,
|
|
50
|
+
onConfirmOverwrite: async () => {
|
|
51
|
+
const answer = await confirm({
|
|
52
|
+
message: `The directory ${resolvedInput.targetDir} is not empty. Continue anyway?`,
|
|
53
|
+
initialValue: false
|
|
54
|
+
});
|
|
55
|
+
if (isCancel(answer)) {
|
|
56
|
+
throw new Error("Scaffolding cancelled by user.");
|
|
57
|
+
}
|
|
58
|
+
return answer;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
outro(createNextStepsMessage(packageManager, resolvedInput.targetDir));
|
|
62
|
+
} catch (error) {
|
|
63
|
+
cancel(error instanceof Error ? error.message : "Scaffolding failed.");
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
cli.help();
|
|
68
|
+
cli.version(packageVersion);
|
|
69
|
+
cli.parse();
|
|
70
|
+
}
|
|
71
|
+
async function promptForProject(targetDir) {
|
|
72
|
+
const targetInput = targetDir?.trim() ? targetDir : await promptText("Project name:", "eventus-mini-app");
|
|
73
|
+
const resolvedInput = resolveProjectInput(targetInput);
|
|
74
|
+
const validationErrors = validateProjectName(resolvedInput.packageName);
|
|
75
|
+
if (validationErrors.length > 0) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Invalid project name "${resolvedInput.packageName}": ${validationErrors.join("; ")}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const appId = toAppId(resolvedInput.packageName);
|
|
81
|
+
if (!appId) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Unable to derive a valid Eventus appId from "${resolvedInput.packageName}".`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return resolvedInput;
|
|
87
|
+
}
|
|
88
|
+
async function resolvePackageManager(packageManagerOption) {
|
|
89
|
+
if (packageManagerOption) {
|
|
90
|
+
if (!isPackageManager(packageManagerOption)) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Unsupported package manager "${packageManagerOption}". Expected one of: ${PACKAGE_MANAGERS.join(", ")}.`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return packageManagerOption;
|
|
96
|
+
}
|
|
97
|
+
const answer = await select({
|
|
98
|
+
message: "Which package manager should the next steps use?",
|
|
99
|
+
options: PACKAGE_MANAGERS.map((packageManager) => ({
|
|
100
|
+
value: packageManager,
|
|
101
|
+
label: packageManager
|
|
102
|
+
})),
|
|
103
|
+
initialValue: "pnpm"
|
|
104
|
+
});
|
|
105
|
+
if (isCancel(answer)) {
|
|
106
|
+
throw new Error("Scaffolding cancelled by user.");
|
|
107
|
+
}
|
|
108
|
+
return answer;
|
|
109
|
+
}
|
|
110
|
+
async function promptText(message, initialValue) {
|
|
111
|
+
const answer = await text({
|
|
112
|
+
message,
|
|
113
|
+
initialValue,
|
|
114
|
+
placeholder: initialValue
|
|
115
|
+
});
|
|
116
|
+
if (isCancel(answer)) {
|
|
117
|
+
throw new Error("Scaffolding cancelled by user.");
|
|
118
|
+
}
|
|
119
|
+
return answer.trim();
|
|
120
|
+
}
|
|
121
|
+
void run();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PackageManager } from './utils.js';
|
|
2
|
+
|
|
3
|
+
type ScaffoldProjectOptions = {
|
|
4
|
+
targetDir: string;
|
|
5
|
+
packageName: string;
|
|
6
|
+
displayName: string;
|
|
7
|
+
appId: string;
|
|
8
|
+
packageManager: PackageManager;
|
|
9
|
+
sdkVersion: string;
|
|
10
|
+
vitePluginVersion: string;
|
|
11
|
+
force?: boolean | undefined;
|
|
12
|
+
templateDir?: string | undefined;
|
|
13
|
+
onConfirmOverwrite?: (() => Promise<boolean>) | undefined;
|
|
14
|
+
};
|
|
15
|
+
declare function scaffoldProject(options: ScaffoldProjectOptions): Promise<void>;
|
|
16
|
+
|
|
17
|
+
export { type ScaffoldProjectOptions, scaffoldProject };
|
package/dist/scaffold.js
ADDED
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const PACKAGE_MANAGERS: readonly ["pnpm", "npm", "yarn", "bun"];
|
|
2
|
+
type PackageManager = (typeof PACKAGE_MANAGERS)[number];
|
|
3
|
+
type ResolvedProjectInput = {
|
|
4
|
+
input: string;
|
|
5
|
+
targetDir: string;
|
|
6
|
+
directoryName: string;
|
|
7
|
+
packageName: string;
|
|
8
|
+
};
|
|
9
|
+
declare function resolveProjectInput(input: string, cwd?: string): ResolvedProjectInput;
|
|
10
|
+
declare function validateProjectName(name: string): string[];
|
|
11
|
+
declare function toDisplayName(projectName: string): string;
|
|
12
|
+
declare function toAppId(projectName: string): string;
|
|
13
|
+
declare function isPackageManager(value: string): value is PackageManager;
|
|
14
|
+
declare function readToolkitVersion(packageFileUrl: URL): Promise<string>;
|
|
15
|
+
declare function createNextStepsMessage(packageManager: PackageManager, projectDirectory: string): string;
|
|
16
|
+
|
|
17
|
+
export { PACKAGE_MANAGERS, type PackageManager, type ResolvedProjectInput, createNextStepsMessage, isPackageManager, readToolkitVersion, resolveProjectInput, toAppId, toDisplayName, validateProjectName };
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PACKAGE_MANAGERS,
|
|
3
|
+
createNextStepsMessage,
|
|
4
|
+
isPackageManager,
|
|
5
|
+
readToolkitVersion,
|
|
6
|
+
resolveProjectInput,
|
|
7
|
+
toAppId,
|
|
8
|
+
toDisplayName,
|
|
9
|
+
validateProjectName
|
|
10
|
+
} from "./chunk-BFKUHPSY.js";
|
|
11
|
+
export {
|
|
12
|
+
PACKAGE_MANAGERS,
|
|
13
|
+
createNextStepsMessage,
|
|
14
|
+
isPackageManager,
|
|
15
|
+
readToolkitVersion,
|
|
16
|
+
resolveProjectInput,
|
|
17
|
+
toAppId,
|
|
18
|
+
toDisplayName,
|
|
19
|
+
validateProjectName
|
|
20
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-eventus-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold an Eventus mini app.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-eventus-app": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./scaffold": {
|
|
18
|
+
"types": "./dist/scaffold.d.ts",
|
|
19
|
+
"import": "./dist/scaffold.js"
|
|
20
|
+
},
|
|
21
|
+
"./utils": {
|
|
22
|
+
"types": "./dist/utils.d.ts",
|
|
23
|
+
"import": "./dist/utils.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"template"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "node ../../scripts/sync-template.mjs && tsup src/index.ts src/scaffold.ts src/utils.ts --format esm --dts --clean",
|
|
32
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
33
|
+
"test": "vitest run"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@clack/prompts": "^0.10.1",
|
|
37
|
+
"cac": "^6.7.14",
|
|
38
|
+
"validate-npm-package-name": "^5.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# __EVENTUS_DISPLAY_NAME__
|
|
2
|
+
|
|
3
|
+
Starter mini app generated for the Eventus platform.
|
|
4
|
+
|
|
5
|
+
## Quickstart
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install
|
|
9
|
+
pnpm dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Available Scripts
|
|
13
|
+
|
|
14
|
+
- `pnpm dev`: start the Vite development server
|
|
15
|
+
- `pnpm build`: type-check and build the app
|
|
16
|
+
- `pnpm preview`: preview the production build locally
|
|
17
|
+
|
|
18
|
+
## Eventus Files
|
|
19
|
+
|
|
20
|
+
- `eventus.manifest.json`: base Eventus manifest consumed by the Vite plugin
|
|
21
|
+
- `src/eventus-env.d.ts`: type declarations for the Eventus virtual module
|
|
22
|
+
- `src/App.tsx`: sample usage of `@eventusgo/sdk` and the manifest module
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>__EVENTUS_DISPLAY_NAME__</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__EVENTUS_PACKAGE_NAME__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc --noEmit && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@eventusgo/sdk": "__EVENTUS_SDK_VERSION__",
|
|
13
|
+
"react": "18.3.1",
|
|
14
|
+
"react-dom": "18.3.1"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@eventusgo/vite-plugin": "__EVENTUS_VITE_PLUGIN_VERSION__",
|
|
18
|
+
"@types/react": "18.3.28",
|
|
19
|
+
"@types/react-dom": "18.3.7",
|
|
20
|
+
"@vitejs/plugin-react": "4.7.0",
|
|
21
|
+
"typescript": "5.9.3",
|
|
22
|
+
"vite": "5.4.21"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import manifest from "virtual:eventus-manifest";
|
|
3
|
+
import {
|
|
4
|
+
eventus,
|
|
5
|
+
type EventusContext,
|
|
6
|
+
type EventusRuntimeSource,
|
|
7
|
+
} from "@eventusgo/sdk";
|
|
8
|
+
|
|
9
|
+
type LoadState =
|
|
10
|
+
| { status: "idle" | "loading" }
|
|
11
|
+
| { status: "ready"; context: EventusContext }
|
|
12
|
+
| { status: "error"; message: string };
|
|
13
|
+
|
|
14
|
+
export default function App() {
|
|
15
|
+
const [state, setState] = useState<LoadState>({ status: "loading" });
|
|
16
|
+
const [runtimeSource, setRuntimeSource] = useState<EventusRuntimeSource | "loading">(
|
|
17
|
+
"loading",
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
let cancelled = false;
|
|
22
|
+
let receivedReadyEvent = false;
|
|
23
|
+
|
|
24
|
+
const unsubscribeReady = eventus.on("ready", ({ context, source }) => {
|
|
25
|
+
receivedReadyEvent = true;
|
|
26
|
+
if (!cancelled) {
|
|
27
|
+
setRuntimeSource(source);
|
|
28
|
+
setState({ status: "ready", context });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const unsubscribe = eventus.on("themeChange", ({ context }) => {
|
|
33
|
+
if (!cancelled) {
|
|
34
|
+
setState({ status: "ready", context });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
void (async () => {
|
|
39
|
+
try {
|
|
40
|
+
await eventus.ready();
|
|
41
|
+
const context = await eventus.getContext();
|
|
42
|
+
|
|
43
|
+
if (!cancelled && !receivedReadyEvent) {
|
|
44
|
+
setRuntimeSource("mock");
|
|
45
|
+
setState({ status: "ready", context });
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (!cancelled) {
|
|
49
|
+
setState({
|
|
50
|
+
status: "error",
|
|
51
|
+
message:
|
|
52
|
+
error instanceof Error
|
|
53
|
+
? error.message
|
|
54
|
+
: "Unable to initialize the Eventus client.",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
cancelled = true;
|
|
62
|
+
unsubscribeReady();
|
|
63
|
+
unsubscribe();
|
|
64
|
+
};
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
67
|
+
const runtimeLabel =
|
|
68
|
+
runtimeSource === "native"
|
|
69
|
+
? "Native bridge detected"
|
|
70
|
+
: runtimeSource === "mock"
|
|
71
|
+
? "Browser mock mode active"
|
|
72
|
+
: "Detecting runtime…";
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<main className="app-shell">
|
|
76
|
+
<section className="card">
|
|
77
|
+
<p className="eyebrow">Eventus Mini App Toolkit</p>
|
|
78
|
+
<h1>{manifest.name}</h1>
|
|
79
|
+
<p className="subtitle">
|
|
80
|
+
App ID <code>{manifest.appId}</code> · Version <code>{manifest.version}</code>
|
|
81
|
+
</p>
|
|
82
|
+
|
|
83
|
+
<div className="status-banner">{runtimeLabel}</div>
|
|
84
|
+
|
|
85
|
+
{state.status === "loading" ? (
|
|
86
|
+
<p>Connecting to the Eventus client…</p>
|
|
87
|
+
) : null}
|
|
88
|
+
|
|
89
|
+
{state.status === "error" ? (
|
|
90
|
+
<p className="error-text">{state.message}</p>
|
|
91
|
+
) : null}
|
|
92
|
+
|
|
93
|
+
{state.status === "ready" ? (
|
|
94
|
+
<dl className="details-grid">
|
|
95
|
+
<div>
|
|
96
|
+
<dt>Theme</dt>
|
|
97
|
+
<dd>{state.context.theme}</dd>
|
|
98
|
+
</div>
|
|
99
|
+
<div>
|
|
100
|
+
<dt>Locale</dt>
|
|
101
|
+
<dd>{state.context.locale}</dd>
|
|
102
|
+
</div>
|
|
103
|
+
<div>
|
|
104
|
+
<dt>User</dt>
|
|
105
|
+
<dd>{state.context.user?.name ?? state.context.user?.id ?? "Anonymous"}</dd>
|
|
106
|
+
</div>
|
|
107
|
+
<div>
|
|
108
|
+
<dt>Manifest Permissions</dt>
|
|
109
|
+
<dd>{manifest.permissions?.join(", ") ?? "None"}</dd>
|
|
110
|
+
</div>
|
|
111
|
+
</dl>
|
|
112
|
+
) : null}
|
|
113
|
+
|
|
114
|
+
{state.status === "ready" ? (
|
|
115
|
+
<pre className="context-preview">
|
|
116
|
+
{JSON.stringify(state.context, null, 2)}
|
|
117
|
+
</pre>
|
|
118
|
+
) : null}
|
|
119
|
+
</section>
|
|
120
|
+
</main>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
3
|
+
type EventusManifest = {
|
|
4
|
+
name: string;
|
|
5
|
+
appId: string;
|
|
6
|
+
version: string;
|
|
7
|
+
permissions?: string[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
declare module "virtual:eventus-manifest" {
|
|
11
|
+
const manifest: EventusManifest;
|
|
12
|
+
export default manifest;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare const __EVENTUS_MANIFEST__: EventusManifest;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
|
|
3
|
+
color: #102347;
|
|
4
|
+
background:
|
|
5
|
+
radial-gradient(circle at top, #d8f2ff 0%, #f3f9ff 35%, #eef3ff 100%);
|
|
6
|
+
line-height: 1.5;
|
|
7
|
+
font-weight: 400;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
* {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
margin: 0;
|
|
16
|
+
min-width: 320px;
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
code,
|
|
21
|
+
pre {
|
|
22
|
+
font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.app-shell {
|
|
26
|
+
min-height: 100vh;
|
|
27
|
+
display: grid;
|
|
28
|
+
place-items: center;
|
|
29
|
+
padding: 24px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.card {
|
|
33
|
+
width: min(720px, 100%);
|
|
34
|
+
padding: 32px;
|
|
35
|
+
border-radius: 24px;
|
|
36
|
+
background: rgba(255, 255, 255, 0.84);
|
|
37
|
+
border: 1px solid rgba(16, 35, 71, 0.08);
|
|
38
|
+
box-shadow: 0 24px 80px rgba(38, 73, 134, 0.14);
|
|
39
|
+
backdrop-filter: blur(16px);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.eyebrow {
|
|
43
|
+
margin: 0 0 8px;
|
|
44
|
+
font-size: 0.85rem;
|
|
45
|
+
letter-spacing: 0.12em;
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
color: #3f6ea8;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
h1 {
|
|
51
|
+
margin: 0;
|
|
52
|
+
font-size: clamp(2rem, 4vw, 3rem);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.subtitle {
|
|
56
|
+
margin: 12px 0 0;
|
|
57
|
+
color: #465d82;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.status-banner {
|
|
61
|
+
margin: 24px 0;
|
|
62
|
+
display: inline-flex;
|
|
63
|
+
padding: 8px 12px;
|
|
64
|
+
border-radius: 999px;
|
|
65
|
+
background: #eef6ff;
|
|
66
|
+
color: #22548d;
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.details-grid {
|
|
71
|
+
display: grid;
|
|
72
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
73
|
+
gap: 16px;
|
|
74
|
+
margin: 24px 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.details-grid div {
|
|
78
|
+
padding: 16px;
|
|
79
|
+
border-radius: 16px;
|
|
80
|
+
background: #f8fbff;
|
|
81
|
+
border: 1px solid rgba(16, 35, 71, 0.08);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
dt {
|
|
85
|
+
margin-bottom: 6px;
|
|
86
|
+
font-size: 0.85rem;
|
|
87
|
+
color: #6880a6;
|
|
88
|
+
text-transform: uppercase;
|
|
89
|
+
letter-spacing: 0.06em;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
dd {
|
|
93
|
+
margin: 0;
|
|
94
|
+
font-weight: 600;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.context-preview {
|
|
98
|
+
margin: 0;
|
|
99
|
+
padding: 16px;
|
|
100
|
+
border-radius: 16px;
|
|
101
|
+
overflow: auto;
|
|
102
|
+
background: #0d1d3a;
|
|
103
|
+
color: #eef5ff;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.error-text {
|
|
107
|
+
color: #b42318;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@media (max-width: 640px) {
|
|
111
|
+
.card {
|
|
112
|
+
padding: 24px;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
6
|
+
"allowJs": false,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"esModuleInterop": false,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"module": "ESNext",
|
|
13
|
+
"moduleResolution": "Bundler",
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx"
|
|
18
|
+
},
|
|
19
|
+
"include": ["src", "vite.config.ts"]
|
|
20
|
+
}
|