@mayson-org/inject-script 1.0.14 → 1.0.16
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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/plugins/metadata/__tests__/api-handler.test.d.ts +2 -0
- package/dist/plugins/metadata/__tests__/api-handler.test.d.ts.map +1 -0
- package/dist/plugins/metadata/__tests__/api-handler.test.js +46 -0
- package/dist/plugins/metadata/api-handler.d.ts +18 -0
- package/dist/plugins/metadata/api-handler.d.ts.map +1 -0
- package/dist/plugins/metadata/api-handler.js +87 -0
- package/dist/plugins/metadata/index.d.ts +8 -0
- package/dist/plugins/metadata/index.d.ts.map +1 -1
- package/dist/plugins/metadata/index.js +50 -3
- package/dist/plugins/metadata/inject-api-route.d.ts +9 -0
- package/dist/plugins/metadata/inject-api-route.d.ts.map +1 -0
- package/dist/plugins/metadata/inject-api-route.js +49 -0
- package/package.json +3 -3
- package/.env.example +0 -12
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export type { MaysonEnvConfig } from './shared/config.js';
|
|
|
10
10
|
export { fetchGeneratedAppMetadata, resolveMaysonConfig, shouldFetchGeneratedAppMetadata, } from './shared/generated-app-metadata.js';
|
|
11
11
|
export { watermarkPlugin, buildMaysonWatermarkSource, } from './plugins/watermark/index.js';
|
|
12
12
|
export { asciiPlugin, buildMaysonAsciiSource, } from './plugins/ascii/index.js';
|
|
13
|
+
export { metadataPlugin, handleMetadataApiRoute, generateMetadataApiRouteSource, injectMetadataApiRoute, METADATA_COOKIE_NAME, DEFAULT_COOKIE_MAX_AGE, } from './plugins/metadata/index.js';
|
|
14
|
+
export type { MetadataApiResponse, MaysonRealtimeStats } from './plugins/metadata/index.js';
|
|
13
15
|
export { pwaPlugin, injectPwaProject, buildMaysonServiceWorkerRegisterSource, buildMaysonInstallPromptSource, buildManifestSource, buildSwJsSource, patchLayoutPwaMetadata, } from './plugins/pwa/index.js';
|
|
14
16
|
export { BUILD_WITH_LOVE_ASCII, MAYSON_LETTER_ASCII, } from './shared/ascii-art.js';
|
|
15
17
|
export type { ComponentType, GeneratedComponentInfo, ComponentGeneratorOptions, InjectorOptions, InjectorResult, MaysonPlugin, PluginGenerateContext, ProjectInjectContext, ProjectInjectResult, } from './core/types.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EAEf,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,+BAA+B,GAChC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,eAAe,EACf,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,WAAW,EACX,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,SAAS,EACT,gBAAgB,EAChB,sCAAsC,EACtC,8BAA8B,EAC9B,mBAAmB,EACnB,eAAe,EACf,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,aAAa,EACb,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAKzB,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AA4EF;;;GAGG;AACH,wBAAsB,aAAa,CACjC,WAAW,GAAE,MAAsB,EACnC,cAAc,GAAE,aAAa,EAAE,GAAG,oBAAoC,EACtE,SAAS,GAAE,OAAe,GACzB,OAAO,CAAC,cAAc,CAAC,CA+GzB"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,aAAa,EACb,cAAc,EAEf,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,+BAA+B,GAChC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,eAAe,EACf,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,WAAW,EACX,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,8BAA8B,EAC9B,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAC5F,OAAO,EACL,SAAS,EACT,gBAAgB,EAChB,sCAAsC,EACtC,8BAA8B,EAC9B,mBAAmB,EACnB,eAAe,EACf,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,aAAa,EACb,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAKzB,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AA4EF;;;GAGG;AACH,wBAAsB,aAAa,CACjC,WAAW,GAAE,MAAsB,EACnC,cAAc,GAAE,aAAa,EAAE,GAAG,oBAAoC,EACtE,SAAS,GAAE,OAAe,GACzB,OAAO,CAAC,cAAc,CAAC,CA+GzB"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export { loadMaysonEnvConfig } from './shared/config.js';
|
|
|
14
14
|
export { fetchGeneratedAppMetadata, resolveMaysonConfig, shouldFetchGeneratedAppMetadata, } from './shared/generated-app-metadata.js';
|
|
15
15
|
export { watermarkPlugin, buildMaysonWatermarkSource, } from './plugins/watermark/index.js';
|
|
16
16
|
export { asciiPlugin, buildMaysonAsciiSource, } from './plugins/ascii/index.js';
|
|
17
|
+
export { metadataPlugin, handleMetadataApiRoute, generateMetadataApiRouteSource, injectMetadataApiRoute, METADATA_COOKIE_NAME, DEFAULT_COOKIE_MAX_AGE, } from './plugins/metadata/index.js';
|
|
17
18
|
export { pwaPlugin, injectPwaProject, buildMaysonServiceWorkerRegisterSource, buildMaysonInstallPromptSource, buildManifestSource, buildSwJsSource, patchLayoutPwaMetadata, } from './plugins/pwa/index.js';
|
|
18
19
|
export { BUILD_WITH_LOVE_ASCII, MAYSON_LETTER_ASCII, } from './shared/ascii-art.js';
|
|
19
20
|
const DEFAULT_TYPES = ['watermark', 'ascii', 'pwa'];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-handler.test.d.ts","sourceRoot":"","sources":["../../../../src/plugins/metadata/__tests__/api-handler.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { handleMetadataApiRoute, METADATA_COOKIE_NAME, generateMetadataApiRouteSource, } from '../api-handler.js';
|
|
4
|
+
describe('Metadata API Route & Cookie Caching', () => {
|
|
5
|
+
it('returns cache MISS on initial request without cookie', async () => {
|
|
6
|
+
const req = new Request('http://localhost:3000/api/randomeapipath', {
|
|
7
|
+
method: 'GET',
|
|
8
|
+
});
|
|
9
|
+
const res = await handleMetadataApiRoute(req);
|
|
10
|
+
assert.equal(res.status, 200);
|
|
11
|
+
assert.equal(res.headers.get('X-Mayson-Cache'), 'MISS');
|
|
12
|
+
assert.ok(res.headers.get('Set-Cookie')?.includes(METADATA_COOKIE_NAME));
|
|
13
|
+
const data = (await res.json());
|
|
14
|
+
assert.equal(data.success, true);
|
|
15
|
+
assert.equal(data.cached, false);
|
|
16
|
+
assert.equal(typeof data.data.showRemixPill, 'boolean');
|
|
17
|
+
});
|
|
18
|
+
it('returns cache HIT when valid cookie is provided', async () => {
|
|
19
|
+
const cachedData = {
|
|
20
|
+
showRemixPill: true,
|
|
21
|
+
enableRemix: false,
|
|
22
|
+
websiteTitle: 'Test App',
|
|
23
|
+
};
|
|
24
|
+
const cookieValue = `${METADATA_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(cachedData))}`;
|
|
25
|
+
const req = new Request('http://localhost:3000/api/randomeapipath', {
|
|
26
|
+
method: 'GET',
|
|
27
|
+
headers: {
|
|
28
|
+
Cookie: cookieValue,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const res = await handleMetadataApiRoute(req);
|
|
32
|
+
assert.equal(res.status, 200);
|
|
33
|
+
assert.equal(res.headers.get('X-Mayson-Cache'), 'HIT');
|
|
34
|
+
const data = (await res.json());
|
|
35
|
+
assert.equal(data.success, true);
|
|
36
|
+
assert.equal(data.cached, true);
|
|
37
|
+
assert.equal(data.data.showRemixPill, true);
|
|
38
|
+
assert.equal(data.data.enableRemix, false);
|
|
39
|
+
assert.equal(data.data.websiteTitle, 'Test App');
|
|
40
|
+
});
|
|
41
|
+
it('generates valid App Router route handler source code', () => {
|
|
42
|
+
const source = generateMetadataApiRouteSource();
|
|
43
|
+
assert.ok(source.includes("import { handleMetadataApiRoute } from '@mayson-org/inject-script'"));
|
|
44
|
+
assert.ok(source.includes('export async function GET(request: Request)'));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MaysonEnvConfig } from '../../shared/config.js';
|
|
2
|
+
export declare const METADATA_COOKIE_NAME = "mayson_metadata_cache";
|
|
3
|
+
export declare const DEFAULT_COOKIE_MAX_AGE = 300;
|
|
4
|
+
export type MetadataApiResponse = {
|
|
5
|
+
success: boolean;
|
|
6
|
+
cached: boolean;
|
|
7
|
+
data: Pick<MaysonEnvConfig, 'showRemixPill' | 'enableRemix' | 'websiteTitle'>;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Handle incoming Next.js API Request for metadata with cookie caching.
|
|
12
|
+
*/
|
|
13
|
+
export declare function handleMetadataApiRoute(req: Request): Promise<Response>;
|
|
14
|
+
/**
|
|
15
|
+
* Generate Next.js App Router Route Handler code for /api/randomeapipath/route.ts
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateMetadataApiRouteSource(): string;
|
|
18
|
+
//# sourceMappingURL=api-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-handler.d.ts","sourceRoot":"","sources":["../../../src/plugins/metadata/api-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAC5D,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,eAAe,GAAG,aAAa,GAAG,cAAc,CAAC,CAAC;IAC9E,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAiBF;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsD5E;AAED;;GAEG;AACH,wBAAgB,8BAA8B,IAAI,MAAM,CAWvD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { resolveMaysonConfig } from '../../shared/generated-app-metadata.js';
|
|
2
|
+
export const METADATA_COOKIE_NAME = 'mayson_metadata_cache';
|
|
3
|
+
export const DEFAULT_COOKIE_MAX_AGE = 300; // 5 minutes
|
|
4
|
+
/**
|
|
5
|
+
* Parses cookies from HTTP Cookie header string.
|
|
6
|
+
*/
|
|
7
|
+
function parseCookies(cookieHeader) {
|
|
8
|
+
if (!cookieHeader)
|
|
9
|
+
return {};
|
|
10
|
+
const cookies = {};
|
|
11
|
+
for (const pair of cookieHeader.split(';')) {
|
|
12
|
+
const [key, ...val] = pair.trim().split('=');
|
|
13
|
+
if (key) {
|
|
14
|
+
cookies[key] = decodeURIComponent(val.join('='));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return cookies;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Handle incoming Next.js API Request for metadata with cookie caching.
|
|
21
|
+
*/
|
|
22
|
+
export async function handleMetadataApiRoute(req) {
|
|
23
|
+
const cookieHeader = req.headers.get('cookie');
|
|
24
|
+
const cookies = parseCookies(cookieHeader);
|
|
25
|
+
const cachedValue = cookies[METADATA_COOKIE_NAME];
|
|
26
|
+
if (cachedValue) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = JSON.parse(cachedValue);
|
|
29
|
+
if (typeof parsed.showRemixPill === 'boolean') {
|
|
30
|
+
const payload = {
|
|
31
|
+
success: true,
|
|
32
|
+
cached: true,
|
|
33
|
+
data: parsed,
|
|
34
|
+
timestamp: Date.now(),
|
|
35
|
+
};
|
|
36
|
+
return new Response(JSON.stringify(payload), {
|
|
37
|
+
status: 200,
|
|
38
|
+
headers: {
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
'X-Mayson-Cache': 'HIT',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Invalid cache content — proceed to fetch fresh
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Cache MISS — resolve metadata
|
|
50
|
+
const config = await resolveMaysonConfig();
|
|
51
|
+
const data = {
|
|
52
|
+
showRemixPill: config.showRemixPill,
|
|
53
|
+
enableRemix: config.enableRemix,
|
|
54
|
+
websiteTitle: config.websiteTitle,
|
|
55
|
+
};
|
|
56
|
+
const payload = {
|
|
57
|
+
success: true,
|
|
58
|
+
cached: false,
|
|
59
|
+
data,
|
|
60
|
+
timestamp: Date.now(),
|
|
61
|
+
};
|
|
62
|
+
const serializedData = encodeURIComponent(JSON.stringify(data));
|
|
63
|
+
const setCookieHeader = `${METADATA_COOKIE_NAME}=${serializedData}; Path=/; Max-Age=${DEFAULT_COOKIE_MAX_AGE}; SameSite=Lax; HttpOnly`;
|
|
64
|
+
return new Response(JSON.stringify(payload), {
|
|
65
|
+
status: 200,
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
'Set-Cookie': setCookieHeader,
|
|
69
|
+
'X-Mayson-Cache': 'MISS',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generate Next.js App Router Route Handler code for /api/randomeapipath/route.ts
|
|
75
|
+
*/
|
|
76
|
+
export function generateMetadataApiRouteSource() {
|
|
77
|
+
return `import { handleMetadataApiRoute } from '@mayson-org/inject-script';
|
|
78
|
+
|
|
79
|
+
export async function GET(request: Request) {
|
|
80
|
+
return handleMetadataApiRoute(request);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function POST(request: Request) {
|
|
84
|
+
return handleMetadataApiRoute(request);
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { MaysonPlugin } from '../../core/types.js';
|
|
2
|
+
export { handleMetadataApiRoute, generateMetadataApiRouteSource, METADATA_COOKIE_NAME, DEFAULT_COOKIE_MAX_AGE, } from './api-handler.js';
|
|
3
|
+
export type { MetadataApiResponse } from './api-handler.js';
|
|
4
|
+
export { injectMetadataApiRoute, DEFAULT_METADATA_API_ROUTE_PATH, } from './inject-api-route.js';
|
|
5
|
+
export type MaysonRealtimeStats = {
|
|
6
|
+
showRemixPill: boolean;
|
|
7
|
+
enableRemix: boolean;
|
|
8
|
+
websiteTitle: string;
|
|
9
|
+
};
|
|
2
10
|
export declare const metadataPlugin: MaysonPlugin;
|
|
3
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,EACL,sBAAsB,EACtB,8BAA8B,EAC9B,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EACL,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,MAAM,mBAAmB,GAAG;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAuDF,eAAO,MAAM,cAAc,EAAE,YAK5B,CAAC"}
|
|
@@ -1,9 +1,55 @@
|
|
|
1
|
+
import { injectMetadataApiRoute } from './inject-api-route.js';
|
|
2
|
+
export { handleMetadataApiRoute, generateMetadataApiRouteSource, METADATA_COOKIE_NAME, DEFAULT_COOKIE_MAX_AGE, } from './api-handler.js';
|
|
3
|
+
export { injectMetadataApiRoute, DEFAULT_METADATA_API_ROUTE_PATH, } from './inject-api-route.js';
|
|
1
4
|
const METADATA_SOURCE = `'use client';
|
|
2
|
-
import React from 'react';
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
export type MaysonRealtimeStats = {
|
|
9
|
+
showRemixPill: boolean;
|
|
10
|
+
enableRemix: boolean;
|
|
11
|
+
websiteTitle: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function MaysonMetadata({ apiRoute = '/api/randomeapipath' }: { apiRoute?: string }) {
|
|
15
|
+
const [stats, setStats] = useState<MaysonRealtimeStats | null>(null);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
let isMounted = true;
|
|
19
|
+
async function checkRealtimeStats() {
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch(apiRoute, {
|
|
22
|
+
method: 'GET',
|
|
23
|
+
headers: { Accept: 'application/json' },
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) return;
|
|
26
|
+
const json = await res.json();
|
|
27
|
+
if (json && json.success && json.data && isMounted) {
|
|
28
|
+
setStats(json.data);
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
// Silent catch for network issue
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
checkRealtimeStats();
|
|
35
|
+
return () => {
|
|
36
|
+
isMounted = false;
|
|
37
|
+
};
|
|
38
|
+
}, [apiRoute]);
|
|
39
|
+
|
|
5
40
|
return (
|
|
6
|
-
|
|
41
|
+
<>
|
|
42
|
+
<meta name="mayson-generator" content="Mayson App Platform" />
|
|
43
|
+
{stats && (
|
|
44
|
+
<meta
|
|
45
|
+
name="mayson-realtime-stats"
|
|
46
|
+
content={JSON.stringify({
|
|
47
|
+
showRemixPill: stats.showRemixPill,
|
|
48
|
+
enableRemix: stats.enableRemix,
|
|
49
|
+
})}
|
|
50
|
+
/>
|
|
51
|
+
)}
|
|
52
|
+
</>
|
|
7
53
|
);
|
|
8
54
|
}
|
|
9
55
|
`;
|
|
@@ -11,4 +57,5 @@ export const metadataPlugin = {
|
|
|
11
57
|
type: 'metadata',
|
|
12
58
|
name: 'MaysonMetadata',
|
|
13
59
|
generateSource: () => METADATA_SOURCE,
|
|
60
|
+
injectProject: (context) => injectMetadataApiRoute(context),
|
|
14
61
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProjectInjectContext, ProjectInjectResult } from '../../core/types.js';
|
|
2
|
+
export declare const DEFAULT_METADATA_API_ROUTE_PATH = "app/api/randomeapipath/route.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Inject the metadata API route handler (/api/randomeapipath) into the project workspace.
|
|
5
|
+
*/
|
|
6
|
+
export declare function injectMetadataApiRoute(context: ProjectInjectContext & {
|
|
7
|
+
routeSubpath?: string;
|
|
8
|
+
}): ProjectInjectResult;
|
|
9
|
+
//# sourceMappingURL=inject-api-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject-api-route.d.ts","sourceRoot":"","sources":["../../../src/plugins/metadata/inject-api-route.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAGrF,eAAO,MAAM,+BAA+B,oCAAoC,CAAC;AAEjF;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,oBAAoB,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD,mBAAmB,CAkDrB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { generateMetadataApiRouteSource } from './api-handler.js';
|
|
4
|
+
export const DEFAULT_METADATA_API_ROUTE_PATH = 'app/api/randomeapipath/route.ts';
|
|
5
|
+
/**
|
|
6
|
+
* Inject the metadata API route handler (/api/randomeapipath) into the project workspace.
|
|
7
|
+
*/
|
|
8
|
+
export function injectMetadataApiRoute(context) {
|
|
9
|
+
const { projectRoot, layoutPath, dryRun = false, routeSubpath = 'randomeapipath' } = context;
|
|
10
|
+
const messages = [];
|
|
11
|
+
const injectedComponents = [];
|
|
12
|
+
const skippedComponents = [];
|
|
13
|
+
// Determine App Router root directory (app/ or src/app/)
|
|
14
|
+
let appDir = path.join(projectRoot, 'app');
|
|
15
|
+
if (layoutPath && layoutPath.includes(`${path.sep}src${path.sep}app`)) {
|
|
16
|
+
appDir = path.join(projectRoot, 'src', 'app');
|
|
17
|
+
}
|
|
18
|
+
else if (!fs.existsSync(appDir) && fs.existsSync(path.join(projectRoot, 'src', 'app'))) {
|
|
19
|
+
appDir = path.join(projectRoot, 'src', 'app');
|
|
20
|
+
}
|
|
21
|
+
const apiRouteDir = path.join(appDir, 'api', routeSubpath);
|
|
22
|
+
const isTs = layoutPath ? layoutPath.endsWith('.ts') || layoutPath.endsWith('.tsx') : true;
|
|
23
|
+
const fileName = isTs ? 'route.ts' : 'route.js';
|
|
24
|
+
const filePath = path.join(apiRouteDir, fileName);
|
|
25
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
26
|
+
if (fs.existsSync(filePath)) {
|
|
27
|
+
skippedComponents.push(`MetadataApiRoute (${relativePath})`);
|
|
28
|
+
messages.push(`Metadata API route already exists at ${relativePath}`);
|
|
29
|
+
return {
|
|
30
|
+
injectedComponents,
|
|
31
|
+
skippedComponents,
|
|
32
|
+
messages,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const code = generateMetadataApiRouteSource();
|
|
36
|
+
if (!dryRun) {
|
|
37
|
+
fs.mkdirSync(apiRouteDir, { recursive: true });
|
|
38
|
+
fs.writeFileSync(filePath, code, 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
injectedComponents.push(`MetadataApiRoute (${relativePath})`);
|
|
41
|
+
messages.push(dryRun
|
|
42
|
+
? `would write metadata API route to ${relativePath}`
|
|
43
|
+
: `injected metadata API route to ${relativePath}`);
|
|
44
|
+
return {
|
|
45
|
+
injectedComponents,
|
|
46
|
+
skippedComponents,
|
|
47
|
+
messages,
|
|
48
|
+
};
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mayson-org/inject-script",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "CLI and library to inject Mayson watermark, ascii, and PWA into Next.js App Router layouts.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"build": "tsc",
|
|
20
20
|
"test": "node --test",
|
|
21
21
|
"prepublishOnly": "npm run build",
|
|
22
|
-
"publish
|
|
22
|
+
"publish": "npm publish --access public"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"bin",
|
|
@@ -50,4 +50,4 @@
|
|
|
50
50
|
"@types/node": "^26.6.1",
|
|
51
51
|
"typescript": "^5.9.3"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|
package/.env.example
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# Set before running inject-script (e.g. in CI / Vercel build env)
|
|
2
|
-
|
|
3
|
-
MAYSON_WORKSPACE_ID=
|
|
4
|
-
MAYSON_COLLECTION_ID=
|
|
5
|
-
MAYSON_API_BASE_URL=
|
|
6
|
-
MAYSON_APP_BASE_URL=https://mayson.dev
|
|
7
|
-
|
|
8
|
-
# Auth for generated-app-metadata (show_remix_pill / enable_remix / website_title)
|
|
9
|
-
MAYSON_WEB_TOKEN=
|
|
10
|
-
|
|
11
|
-
# Optional PWA/manifest name (else API website_title, else "My App")
|
|
12
|
-
# MAYSON_WEBSITE_TITLE=
|