create-maestro-app 0.0.2
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/LICENSE +193 -0
- package/README.md +62 -0
- package/dist/args.d.ts +28 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +102 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +76 -0
- package/dist/bin.js.map +1 -0
- package/dist/capabilities.d.ts +55 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +75 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/commands.d.ts +17 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +19 -0
- package/dist/commands.js.map +1 -0
- package/dist/credentials.d.ts +34 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +72 -0
- package/dist/credentials.js.map +1 -0
- package/dist/flow.d.ts +37 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/flow.js +177 -0
- package/dist/flow.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/io.d.ts +29 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +79 -0
- package/dist/io.js.map +1 -0
- package/dist/platform-client.d.ts +64 -0
- package/dist/platform-client.d.ts.map +1 -0
- package/dist/platform-client.js +76 -0
- package/dist/platform-client.js.map +1 -0
- package/dist/project.d.ts +26 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +40 -0
- package/dist/project.js.map +1 -0
- package/dist/scaffold.d.ts +36 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +67 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/session.d.ts +24 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +73 -0
- package/dist/session.js.map +1 -0
- package/package.json +34 -0
- package/templates/app/README.md +44 -0
- package/templates/app/_env.example +5 -0
- package/templates/app/_gitignore +15 -0
- package/templates/app/index.html +12 -0
- package/templates/app/maestro.config.ts +13 -0
- package/templates/app/package.json +25 -0
- package/templates/app/src/App.vue +19 -0
- package/templates/app/src/main.ts +6 -0
- package/templates/app/src/vite-env.d.ts +11 -0
- package/templates/app/tsconfig.json +17 -0
- package/templates/app/vite.config.ts +17 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export class PlatformRequestError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
error;
|
|
4
|
+
constructor(status, error) {
|
|
5
|
+
super(error.message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.error = error;
|
|
8
|
+
this.name = 'PlatformRequestError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
async function parseError(res) {
|
|
12
|
+
try {
|
|
13
|
+
const body = (await res.json());
|
|
14
|
+
if (body?.error?.code)
|
|
15
|
+
return body.error;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// fall through to a synthetic envelope
|
|
19
|
+
}
|
|
20
|
+
return { code: 'INTERNAL', message: res.statusText || 'Request failed' };
|
|
21
|
+
}
|
|
22
|
+
export class PlatformClient {
|
|
23
|
+
baseUrl;
|
|
24
|
+
fetchImpl;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, '');
|
|
27
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
28
|
+
}
|
|
29
|
+
async request(method, path, init) {
|
|
30
|
+
const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
31
|
+
method,
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
...(init?.accessToken ? { Authorization: `Bearer ${init.accessToken}` } : {}),
|
|
35
|
+
},
|
|
36
|
+
body: init?.body === undefined ? undefined : JSON.stringify(init.body),
|
|
37
|
+
});
|
|
38
|
+
if (!res.ok)
|
|
39
|
+
throw new PlatformRequestError(res.status, await parseError(res));
|
|
40
|
+
if (res.status === 204)
|
|
41
|
+
return undefined;
|
|
42
|
+
return (await res.json());
|
|
43
|
+
}
|
|
44
|
+
// --- Auth (spec 01) -------------------------------------------------------
|
|
45
|
+
login(email, password) {
|
|
46
|
+
return this.request('POST', '/v1/platform/auth/login', {
|
|
47
|
+
body: { email, password },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
refresh(refreshToken) {
|
|
51
|
+
return this.request('POST', '/v1/platform/auth/refresh', { body: { refreshToken } });
|
|
52
|
+
}
|
|
53
|
+
async logout(refreshToken) {
|
|
54
|
+
await this.request('POST', '/v1/platform/auth/logout', { body: { refreshToken } });
|
|
55
|
+
}
|
|
56
|
+
// --- App templates (spec 16 US-16.1) --------------------------------------
|
|
57
|
+
async listTemplates(accessToken) {
|
|
58
|
+
const res = await this.request('GET', '/v1/platform/app-templates', { accessToken });
|
|
59
|
+
return res.data;
|
|
60
|
+
}
|
|
61
|
+
// --- App creation (spec 16 US-16.2) ---------------------------------------
|
|
62
|
+
createApp(accessToken, input) {
|
|
63
|
+
return this.request('POST', '/v1/platform/apps', {
|
|
64
|
+
accessToken,
|
|
65
|
+
body: input,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// --- Apply an additional blueprint to an existing app (spec 16 US-16.3) ----
|
|
69
|
+
applyTemplate(accessToken, appKey, input) {
|
|
70
|
+
return this.request('POST', `/v1/platform/apps/${appKey}/templates:apply`, {
|
|
71
|
+
accessToken,
|
|
72
|
+
body: input,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=platform-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-client.js","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"AAeA,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAElC;IACA;IAFX,YACW,MAAc,EACd,KAAoB;QAE7B,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHZ,WAAM,GAAN,MAAM,CAAQ;QACd,UAAK,GAAL,KAAK,CAAe;QAG7B,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAuBD,KAAK,UAAU,UAAU,CAAC,GAAa;IACrC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA8B,CAAC;QAC7D,IAAI,IAAI,EAAE,KAAK,EAAE,IAAI;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,uCAAuC;IACzC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,IAAI,gBAAgB,EAAE,CAAC;AAC3E,CAAC;AAED,MAAM,OAAO,cAAc;IACR,OAAO,CAAS;IAChB,SAAS,CAAY;IAEtC,YAAY,OAA8B;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC9C,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,MAAc,EACd,IAAY,EACZ,IAA+C;QAE/C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;YACzD,MAAM;YACN,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9E;YACD,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;SACvE,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/E,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,SAAc,CAAC;QAC9C,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAM,CAAC;IACjC,CAAC;IAED,6EAA6E;IAE7E,KAAK,CAAC,KAAa,EAAE,QAAgB;QACnC,OAAO,IAAI,CAAC,OAAO,CAAkB,MAAM,EAAE,yBAAyB,EAAE;YACtE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,YAAoB;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,2BAA2B,EAAE,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,YAAoB;QAC/B,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,0BAA0B,EAAE,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,6EAA6E;IAE7E,KAAK,CAAC,aAAa,CAAC,WAAmB;QACrC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAC5B,KAAK,EACL,4BAA4B,EAC5B,EAAE,WAAW,EAAE,CAChB,CAAC;QACF,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC;IAED,6EAA6E;IAE7E,SAAS,CACP,WAAmB,EACnB,KAA0E;QAE1E,OAAO,IAAI,CAAC,OAAO,CAAa,MAAM,EAAE,mBAAmB,EAAE;YAC3D,WAAW;YACX,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;IACL,CAAC;IAED,8EAA8E;IAE9E,aAAa,CACX,WAAmB,EACnB,MAAc,EACd,KAA6E;QAE7E,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,qBAAqB,MAAM,kBAAkB,EAAE;YACzE,WAAW;YACX,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-name / app-key derivation and the non-secret env the scaffold writes
|
|
3
|
+
* (spec 18 US-18.4/US-18.5). v1 auth is user-JWT, so `.env` carries only
|
|
4
|
+
* non-secret values — no API key is issued (§Decisions 2).
|
|
5
|
+
*/
|
|
6
|
+
/** Derive a valid snake_case app key from a free-form project name. */
|
|
7
|
+
export declare function deriveAppKey(name: string): string;
|
|
8
|
+
export declare function isValidAppKey(key: string): boolean;
|
|
9
|
+
/** The template variables every client template consumes. */
|
|
10
|
+
export interface ProjectVars {
|
|
11
|
+
/** Index signature so a `ProjectVars` is assignable to `ScaffoldVars`. */
|
|
12
|
+
[key: string]: string;
|
|
13
|
+
PROJECT_NAME: string;
|
|
14
|
+
APP_KEY: string;
|
|
15
|
+
APP_NAME: string;
|
|
16
|
+
API_URL: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildProjectVars(input: {
|
|
19
|
+
projectName: string;
|
|
20
|
+
appKey: string;
|
|
21
|
+
appName: string;
|
|
22
|
+
apiUrl: string;
|
|
23
|
+
}): ProjectVars;
|
|
24
|
+
/** The git-ignored `.env` (non-secret in user-JWT mode). */
|
|
25
|
+
export declare function renderDotEnv(vars: ProjectVars): string;
|
|
26
|
+
//# sourceMappingURL=project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUjD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,6DAA6D;AAC7D,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,WAAW,CAOd;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAOtD"}
|
package/dist/project.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-name / app-key derivation and the non-secret env the scaffold writes
|
|
3
|
+
* (spec 18 US-18.4/US-18.5). v1 auth is user-JWT, so `.env` carries only
|
|
4
|
+
* non-secret values — no API key is issued (§Decisions 2).
|
|
5
|
+
*/
|
|
6
|
+
const APP_KEY_RE = /^[a-z][a-z0-9_]{1,62}$/;
|
|
7
|
+
/** Derive a valid snake_case app key from a free-form project name. */
|
|
8
|
+
export function deriveAppKey(name) {
|
|
9
|
+
const key = name
|
|
10
|
+
.trim()
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
13
|
+
.replace(/^_+|_+$/g, '')
|
|
14
|
+
.replace(/_{2,}/g, '_');
|
|
15
|
+
if (!key)
|
|
16
|
+
return 'app';
|
|
17
|
+
const prefixed = /^[a-z]/.test(key) ? key : `app_${key}`;
|
|
18
|
+
return prefixed.slice(0, 63);
|
|
19
|
+
}
|
|
20
|
+
export function isValidAppKey(key) {
|
|
21
|
+
return APP_KEY_RE.test(key);
|
|
22
|
+
}
|
|
23
|
+
export function buildProjectVars(input) {
|
|
24
|
+
return {
|
|
25
|
+
PROJECT_NAME: input.projectName,
|
|
26
|
+
APP_KEY: input.appKey,
|
|
27
|
+
APP_NAME: input.appName,
|
|
28
|
+
API_URL: input.apiUrl,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** The git-ignored `.env` (non-secret in user-JWT mode). */
|
|
32
|
+
export function renderDotEnv(vars) {
|
|
33
|
+
return [
|
|
34
|
+
`VITE_API_BASE_URL=${vars.API_URL}`,
|
|
35
|
+
`VITE_APP_KEY=${vars.APP_KEY}`,
|
|
36
|
+
`VITE_APP_NAME=${vars.APP_NAME}`,
|
|
37
|
+
'',
|
|
38
|
+
].join('\n');
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,GAAG,wBAAwB,CAAC;AAE5C,uEAAuE;AACvE,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,GAAG,GAAG,IAAI;SACb,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,GAAG,EAAE,CAAC;IACzD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAYD,MAAM,UAAU,gBAAgB,CAAC,KAKhC;IACC,OAAO;QACL,YAAY,EAAE,KAAK,CAAC,WAAW;QAC/B,OAAO,EAAE,KAAK,CAAC,MAAM;QACrB,QAAQ,EAAE,KAAK,CAAC,OAAO;QACvB,OAAO,EAAE,KAAK,CAAC,MAAM;KACtB,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,YAAY,CAAC,IAAiB;IAC5C,OAAO;QACL,qBAAqB,IAAI,CAAC,OAAO,EAAE;QACnC,gBAAgB,IAAI,CAAC,OAAO,EAAE;QAC9B,iBAAiB,IAAI,CAAC,QAAQ,EAAE;QAChC,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic template copy + variable substitution (spec 18 US-18.4, NFR
|
|
3
|
+
* "reproducible & offline-safe"). Walks a template directory, substitutes
|
|
4
|
+
* `__PLACEHOLDER__` tokens, and writes the result into the target directory.
|
|
5
|
+
*
|
|
6
|
+
* npm strips `.gitignore` and `.env*` from published tarballs, so templates ship
|
|
7
|
+
* them under safe names that are restored on write (the create-* convention):
|
|
8
|
+
* `_gitignore` → `.gitignore`
|
|
9
|
+
* `_env.example` → `.env.example`
|
|
10
|
+
*/
|
|
11
|
+
export type ScaffoldVars = Record<string, string>;
|
|
12
|
+
/** Replace every `__NAME__` token that has a matching var. Unknown tokens stay. */
|
|
13
|
+
export declare function substitute(content: string, vars: ScaffoldVars): string;
|
|
14
|
+
export interface ScaffoldPlanEntry {
|
|
15
|
+
/** Path relative to the target directory. */
|
|
16
|
+
path: string;
|
|
17
|
+
bytes: number;
|
|
18
|
+
}
|
|
19
|
+
export interface ScaffoldResult {
|
|
20
|
+
files: ScaffoldPlanEntry[];
|
|
21
|
+
}
|
|
22
|
+
export interface ScaffoldOptions {
|
|
23
|
+
templateDir: string;
|
|
24
|
+
targetDir: string;
|
|
25
|
+
vars: ScaffoldVars;
|
|
26
|
+
/** Extra files to write (e.g. a generated `.env`), keyed by target-relative path. */
|
|
27
|
+
extraFiles?: Record<string, string>;
|
|
28
|
+
/** When true, compute the plan without writing anything. */
|
|
29
|
+
dryRun?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Scaffold a project. Returns the ordered list of files that were (or, in
|
|
33
|
+
* `dryRun`, would be) written, so the caller can print a plan.
|
|
34
|
+
*/
|
|
35
|
+
export declare function scaffoldProject(options: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
36
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AASlD,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAItE;AA8BD,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA2BvF"}
|
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, relative } from 'node:path';
|
|
3
|
+
/** Files renamed on write so npm doesn't strip them from the published package. */
|
|
4
|
+
const RENAME_ON_WRITE = {
|
|
5
|
+
_gitignore: '.gitignore',
|
|
6
|
+
'_env.example': '.env.example',
|
|
7
|
+
_npmrc: '.npmrc',
|
|
8
|
+
};
|
|
9
|
+
/** Replace every `__NAME__` token that has a matching var. Unknown tokens stay. */
|
|
10
|
+
export function substitute(content, vars) {
|
|
11
|
+
return content.replace(/__([A-Z0-9_]+)__/g, (match, name) => Object.prototype.hasOwnProperty.call(vars, name) ? vars[name] : match);
|
|
12
|
+
}
|
|
13
|
+
/** Apply the publish-safe rename to a single path segment. */
|
|
14
|
+
function targetName(name) {
|
|
15
|
+
return RENAME_ON_WRITE[name] ?? name;
|
|
16
|
+
}
|
|
17
|
+
async function walk(dir) {
|
|
18
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
19
|
+
const files = [];
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
const full = join(dir, entry.name);
|
|
22
|
+
if (entry.isDirectory()) {
|
|
23
|
+
files.push(...(await walk(full)));
|
|
24
|
+
}
|
|
25
|
+
else if (entry.isFile()) {
|
|
26
|
+
files.push(full);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return files.sort(); // deterministic order
|
|
30
|
+
}
|
|
31
|
+
/** Map a template file's absolute path to its target-relative path (renamed). */
|
|
32
|
+
function toTargetRelative(templateDir, file) {
|
|
33
|
+
const rel = relative(templateDir, file);
|
|
34
|
+
return rel
|
|
35
|
+
.split('/')
|
|
36
|
+
.map((segment) => targetName(segment))
|
|
37
|
+
.join('/');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Scaffold a project. Returns the ordered list of files that were (or, in
|
|
41
|
+
* `dryRun`, would be) written, so the caller can print a plan.
|
|
42
|
+
*/
|
|
43
|
+
export async function scaffoldProject(options) {
|
|
44
|
+
const { templateDir, targetDir, vars, extraFiles, dryRun } = options;
|
|
45
|
+
const templateFiles = await walk(templateDir);
|
|
46
|
+
const planned = new Map();
|
|
47
|
+
for (const file of templateFiles) {
|
|
48
|
+
const rel = toTargetRelative(templateDir, file);
|
|
49
|
+
const raw = await readFile(file, 'utf8');
|
|
50
|
+
planned.set(rel, substitute(raw, vars));
|
|
51
|
+
}
|
|
52
|
+
for (const [rel, content] of Object.entries(extraFiles ?? {})) {
|
|
53
|
+
planned.set(rel, content); // extras win over template files at the same path
|
|
54
|
+
}
|
|
55
|
+
const entries = [...planned.entries()]
|
|
56
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
57
|
+
.map(([path, content]) => ({ path, bytes: Buffer.byteLength(content, 'utf8') }));
|
|
58
|
+
if (!dryRun) {
|
|
59
|
+
for (const [rel, content] of planned) {
|
|
60
|
+
const dest = join(targetDir, rel);
|
|
61
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
62
|
+
await writeFile(dest, content);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { files: entries };
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CredentialStore } from './credentials.js';
|
|
2
|
+
import { PlatformClient, type OperatorSession } from './platform-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Ties the credential store to the platform client (spec 18 US-18.2): a cached
|
|
5
|
+
* refresh token is exchanged for an access token on demand; a fresh login is
|
|
6
|
+
* persisted; logout revokes and clears. The access token stays in memory only.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SessionManager {
|
|
9
|
+
private readonly baseUrl;
|
|
10
|
+
private readonly platform;
|
|
11
|
+
private readonly store;
|
|
12
|
+
private accessToken?;
|
|
13
|
+
constructor(baseUrl: string, platform: PlatformClient, store?: CredentialStore);
|
|
14
|
+
/** Persist a fresh operator login. */
|
|
15
|
+
signIn(email: string, password: string): Promise<OperatorSession>;
|
|
16
|
+
/**
|
|
17
|
+
* Return a usable access token: reuse the in-memory one, else exchange the
|
|
18
|
+
* cached refresh token. Returns undefined when there is no cached session
|
|
19
|
+
* (the caller should then prompt a fresh login).
|
|
20
|
+
*/
|
|
21
|
+
getAccessToken(): Promise<string | undefined>;
|
|
22
|
+
signOut(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAwB,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAElG;;;;GAIG;AACH,qBAAa,cAAc;IAIvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IALxB,OAAO,CAAC,WAAW,CAAC,CAAS;gBAGV,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,cAAc,EACxB,KAAK,GAAE,eAAuC;IAGjE,sCAAsC;IAChC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAWvE;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAuB7C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAY/B"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { CredentialStore } from './credentials.js';
|
|
2
|
+
import { PlatformRequestError } from './platform-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Ties the credential store to the platform client (spec 18 US-18.2): a cached
|
|
5
|
+
* refresh token is exchanged for an access token on demand; a fresh login is
|
|
6
|
+
* persisted; logout revokes and clears. The access token stays in memory only.
|
|
7
|
+
*/
|
|
8
|
+
export class SessionManager {
|
|
9
|
+
baseUrl;
|
|
10
|
+
platform;
|
|
11
|
+
store;
|
|
12
|
+
accessToken;
|
|
13
|
+
constructor(baseUrl, platform, store = new CredentialStore()) {
|
|
14
|
+
this.baseUrl = baseUrl;
|
|
15
|
+
this.platform = platform;
|
|
16
|
+
this.store = store;
|
|
17
|
+
}
|
|
18
|
+
/** Persist a fresh operator login. */
|
|
19
|
+
async signIn(email, password) {
|
|
20
|
+
const session = await this.platform.login(email, password);
|
|
21
|
+
this.accessToken = session.accessToken;
|
|
22
|
+
await this.store.set(this.baseUrl, {
|
|
23
|
+
kind: 'operator_refresh',
|
|
24
|
+
refreshToken: session.refreshToken,
|
|
25
|
+
operator: session.operator,
|
|
26
|
+
});
|
|
27
|
+
return session;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Return a usable access token: reuse the in-memory one, else exchange the
|
|
31
|
+
* cached refresh token. Returns undefined when there is no cached session
|
|
32
|
+
* (the caller should then prompt a fresh login).
|
|
33
|
+
*/
|
|
34
|
+
async getAccessToken() {
|
|
35
|
+
if (this.accessToken)
|
|
36
|
+
return this.accessToken;
|
|
37
|
+
const cached = await this.store.get(this.baseUrl);
|
|
38
|
+
if (!cached)
|
|
39
|
+
return undefined;
|
|
40
|
+
try {
|
|
41
|
+
const tokens = await this.platform.refresh(cached.refreshToken);
|
|
42
|
+
this.accessToken = tokens.accessToken;
|
|
43
|
+
await this.store.set(this.baseUrl, {
|
|
44
|
+
kind: 'operator_refresh',
|
|
45
|
+
refreshToken: tokens.refreshToken,
|
|
46
|
+
operator: cached.operator,
|
|
47
|
+
});
|
|
48
|
+
return tokens.accessToken;
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
// A dead/rotated refresh token ⇒ drop it so the caller re-prompts login.
|
|
52
|
+
if (err instanceof PlatformRequestError && err.status === 401) {
|
|
53
|
+
await this.store.clear(this.baseUrl);
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async signOut() {
|
|
60
|
+
const cached = await this.store.get(this.baseUrl);
|
|
61
|
+
if (cached) {
|
|
62
|
+
try {
|
|
63
|
+
await this.platform.logout(cached.refreshToken);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Best-effort revoke; clear locally regardless.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
this.accessToken = undefined;
|
|
70
|
+
await this.store.clear(this.baseUrl);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAkB,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAElG;;;;GAIG;AACH,MAAM,OAAO,cAAc;IAIN;IACA;IACA;IALX,WAAW,CAAU;IAE7B,YACmB,OAAe,EACf,QAAwB,EACxB,QAAyB,IAAI,eAAe,EAAE;QAF9C,YAAO,GAAP,OAAO,CAAQ;QACf,aAAQ,GAAR,QAAQ,CAAgB;QACxB,UAAK,GAAL,KAAK,CAAyC;IAC9D,CAAC;IAEJ,sCAAsC;IACtC,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,QAAgB;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC3D,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE;YACjC,IAAI,EAAE,kBAAkB;YACxB,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,cAAc;QAClB,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAChE,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;YACtC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE;gBACjC,IAAI,EAAE,kBAAkB;gBACxB,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B,CAAC,CAAC;YACH,OAAO,MAAM,CAAC,WAAW,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,yEAAyE;YACzE,IAAI,GAAG,YAAY,oBAAoB,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC9D,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACrC,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAClD,CAAC;YAAC,MAAM,CAAC;gBACP,gDAAgD;YAClD,CAAC;QACH,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-maestro-app",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Scaffold a client app wired to the hosted Maestro Core.",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-maestro-app": "./dist/bin.js",
|
|
9
|
+
"maestro": "./dist/bin.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"templates"
|
|
14
|
+
],
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.0.0",
|
|
25
|
+
"typescript": "^5.8.0",
|
|
26
|
+
"vitest": "^3.2.0"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.build.json",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"lint": "eslint src",
|
|
32
|
+
"test": "vitest run"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# __APP_NAME__
|
|
2
|
+
|
|
3
|
+
End-User app for the Maestro App **`__APP_KEY__`**, scaffolded by
|
|
4
|
+
[`create-maestro-app`](https://www.npmjs.com/package/create-maestro-app).
|
|
5
|
+
|
|
6
|
+
It is **one integrated app** for every capability you selected (SaaS, site, e-learning, …), all
|
|
7
|
+
applied to the same App on the hosted Core and rendered from `/runtime` through the shared
|
|
8
|
+
`@bemaestro/saas-ui` shell — the same shell the hosted Maestro client uses. Reads from `__API_URL__`.
|
|
9
|
+
|
|
10
|
+
## Develop
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm install
|
|
14
|
+
pnpm dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Sign in with an End-User account of an organization on your App. The shell loads your surfaces and the
|
|
18
|
+
account area (profile, members, plans, billing, activity, notifications).
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
Runtime config lives in `.env` (git-ignored). Copy `.env.example` to `.env` to override:
|
|
23
|
+
|
|
24
|
+
| Variable | Meaning |
|
|
25
|
+
| ------------------- | ---------------------------- |
|
|
26
|
+
| `VITE_API_BASE_URL` | Hosted Maestro Core base URL |
|
|
27
|
+
| `VITE_APP_KEY` | This project's App key |
|
|
28
|
+
| `VITE_APP_NAME` | Display name |
|
|
29
|
+
|
|
30
|
+
### Server-to-server access (optional)
|
|
31
|
+
|
|
32
|
+
This app authenticates End Users via JWT and needs no secret. For a backend integration, create a
|
|
33
|
+
least-privilege, org-scoped API key in the hosted admin console (never commit it).
|
|
34
|
+
|
|
35
|
+
## Managing the App
|
|
36
|
+
|
|
37
|
+
Schema, capabilities, orgs, users, plans and billing are managed on the **hosted Maestro platform**,
|
|
38
|
+
not from this project. This repository is only the End-User app.
|
|
39
|
+
|
|
40
|
+
## Build
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pnpm build # static bundle in dist/, Cloudflare Pages-ready
|
|
44
|
+
```
|
|
@@ -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>__APP_NAME__</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="/src/main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maestro wiring for this app (spec 18). No secrets live here — End Users
|
|
3
|
+
* authenticate via JWT (spec 04). Values below were filled in by
|
|
4
|
+
* `create-maestro-app`.
|
|
5
|
+
*/
|
|
6
|
+
export const maestroConfig = {
|
|
7
|
+
/** Hosted Maestro Core base URL. */
|
|
8
|
+
baseUrl:
|
|
9
|
+
(import.meta.env.VITE_API_BASE_URL as string | undefined)?.replace(/\/$/, '') ?? '__API_URL__',
|
|
10
|
+
/** This project's App key on the platform. */
|
|
11
|
+
app: (import.meta.env.VITE_APP_KEY as string | undefined) ?? '__APP_KEY__',
|
|
12
|
+
appName: (import.meta.env.VITE_APP_NAME as string | undefined) ?? '__APP_NAME__',
|
|
13
|
+
} as const
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "0.0.0",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build && vue-tsc -b",
|
|
9
|
+
"typecheck": "vue-tsc -b --noEmit",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@bemaestro/saas-ui": "^0.0.1",
|
|
14
|
+
"@bemaestro/sdk": "^0.0.1",
|
|
15
|
+
"@bemaestro/views": "^0.0.1",
|
|
16
|
+
"vue": "^3.5.25"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@vitejs/plugin-vue": "^6.0.1",
|
|
20
|
+
"@vue/tsconfig": "^0.8.1",
|
|
21
|
+
"typescript": "~5.8.3",
|
|
22
|
+
"vite": "^7.0.0",
|
|
23
|
+
"vue-tsc": "^3.1.1"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Integrated Maestro app (spec 18). One Vue app for every capability you picked
|
|
4
|
+
* (saas / site / elearning / …), all applied to the same App and rendered from
|
|
5
|
+
* `/runtime`. The shared `@bemaestro/saas-ui` shell — the same one the hosted Maestro
|
|
6
|
+
* client uses — handles End-User auth + account area + the product surfaces
|
|
7
|
+
* (`web`/`pwa`). A public `site` surface, when present, exposes a login/sign-up
|
|
8
|
+
* hand-off into this shell.
|
|
9
|
+
*
|
|
10
|
+
* Owner/creator management (schema, orgs, plans) stays in the hosted Maestro admin
|
|
11
|
+
* — this project is only the End-User app.
|
|
12
|
+
*/
|
|
13
|
+
import { SaasShell } from '@bemaestro/saas-ui'
|
|
14
|
+
import { maestroConfig } from '../maestro.config'
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<SaasShell :config="maestroConfig" mode="web" />
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
5
|
+
"baseUrl": ".",
|
|
6
|
+
"paths": {
|
|
7
|
+
"@/*": ["./src/*"]
|
|
8
|
+
},
|
|
9
|
+
"strict": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"module": "ESNext",
|
|
12
|
+
"moduleResolution": "Bundler",
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"types": ["vite/client"]
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*.ts", "src/**/*.vue", "maestro.config.ts", "vite.config.ts"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import { defineConfig } from 'vite'
|
|
4
|
+
|
|
5
|
+
// Cloudflare Pages-ready static build (spec 18), mirroring the hosted client.
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [vue()],
|
|
8
|
+
resolve: {
|
|
9
|
+
alias: {
|
|
10
|
+
'@': path.resolve(__dirname, './src'),
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
// Source-exported Maestro packages: let Vite compile their .vue on the fly.
|
|
14
|
+
optimizeDeps: {
|
|
15
|
+
exclude: ['@bemaestro/views', '@bemaestro/saas-ui'],
|
|
16
|
+
},
|
|
17
|
+
})
|