@mayson-org/inject-script 1.0.24 → 1.0.26

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.
@@ -45,11 +45,12 @@ describe('Metadata API Route & Cookie Caching', () => {
45
45
  assert.ok(source.includes('export async function GET(req: Request)'));
46
46
  assert.ok(source.includes('mayson_metadata_cache'));
47
47
  });
48
- it('generates random API route subpaths starting with mayson-', () => {
48
+ it('generates unpredictable random 10-character API route subpaths', () => {
49
49
  const subpath1 = generateRandomApiSubpath();
50
50
  const subpath2 = generateRandomApiSubpath();
51
- assert.ok(subpath1.startsWith('mayson-'));
52
- assert.ok(subpath2.startsWith('mayson-'));
51
+ assert.equal(subpath1.length, 10);
52
+ assert.equal(subpath2.length, 10);
53
+ assert.ok(!subpath1.startsWith('mayson-'));
53
54
  assert.notEqual(subpath1, subpath2);
54
55
  });
55
56
  });
@@ -1,6 +1,6 @@
1
1
  import type { MaysonEnvConfig } from '../../shared/config.js';
2
2
  export declare const METADATA_COOKIE_NAME = "mayson_metadata_cache";
3
- export declare const DEFAULT_COOKIE_MAX_AGE = 300;
3
+ export declare const DEFAULT_COOKIE_MAX_AGE = 120;
4
4
  export type MetadataApiResponse = {
5
5
  success: boolean;
6
6
  cached: boolean;
@@ -1,6 +1,6 @@
1
1
  import { resolveMaysonConfig } from '../../shared/generated-app-metadata.js';
2
2
  export const METADATA_COOKIE_NAME = 'mayson_metadata_cache';
3
- export const DEFAULT_COOKIE_MAX_AGE = 300; // 5 minutes
3
+ export const DEFAULT_COOKIE_MAX_AGE = 120; // 2 minutes
4
4
  /**
5
5
  * Parses cookies from HTTP Cookie header string.
6
6
  */
@@ -76,7 +76,7 @@ export async function handleMetadataApiRoute(req) {
76
76
  */
77
77
  export function generateMetadataApiRouteSource() {
78
78
  return `const METADATA_COOKIE_NAME = 'mayson_metadata_cache';
79
- const DEFAULT_COOKIE_MAX_AGE = 300; // 5 minutes
79
+ const DEFAULT_COOKIE_MAX_AGE = 120; // 2 minutes
80
80
 
81
81
  function readEnv(key: string): string {
82
82
  return process.env[key]?.trim() ?? '';
@@ -7,6 +7,6 @@ export type MaysonRealtimeStats = {
7
7
  enableRemix: boolean;
8
8
  websiteTitle: string;
9
9
  };
10
- export declare function buildMaysonMetadataSource(context?: PluginGenerateContext): string;
10
+ export declare function buildMaysonMetadataSource(_context?: PluginGenerateContext): string;
11
11
  export declare const metadataPlugin: MaysonPlugin;
12
12
  //# 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,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG/E,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,wBAAwB,EACxB,yBAAyB,GAC1B,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;AAEF,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAsDjF;AAED,eAAO,MAAM,cAAc,EAAE,YAK5B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG/E,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,wBAAwB,EACxB,yBAAyB,GAC1B,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;AAEF,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAWlF;AAED,eAAO,MAAM,cAAc,EAAE,YAK5B,CAAC"}
@@ -1,57 +1,14 @@
1
1
  import { injectMetadataApiRoute } from './inject-api-route.js';
2
2
  export { handleMetadataApiRoute, generateMetadataApiRouteSource, METADATA_COOKIE_NAME, DEFAULT_COOKIE_MAX_AGE, } from './api-handler.js';
3
3
  export { injectMetadataApiRoute, generateRandomApiSubpath, resolveMetadataApiSubpath, } from './inject-api-route.js';
4
- export function buildMaysonMetadataSource(context) {
5
- const apiRoute = context?.metadata?.apiRoutePath ?? '/api/mayson-metadata';
4
+ export function buildMaysonMetadataSource(_context) {
6
5
  return `'use client';
7
6
 
8
- import React, { useEffect, useState } from 'react';
9
-
10
- export type MaysonRealtimeStats = {
11
- showRemixPill: boolean;
12
- enableRemix: boolean;
13
- websiteTitle: string;
14
- };
15
-
16
- export function MaysonMetadata({ apiRoute = ${JSON.stringify(apiRoute)} }: { apiRoute?: string }) {
17
- const [stats, setStats] = useState<MaysonRealtimeStats | null>(null);
18
-
19
- useEffect(() => {
20
- let isMounted = true;
21
- async function checkRealtimeStats() {
22
- try {
23
- const res = await fetch(apiRoute, {
24
- method: 'GET',
25
- headers: { Accept: 'application/json' },
26
- });
27
- if (!res.ok) return;
28
- const json = await res.json();
29
- if (json && json.success && json.data && isMounted) {
30
- setStats(json.data);
31
- }
32
- } catch {
33
- // Silent catch for network issue
34
- }
35
- }
36
- checkRealtimeStats();
37
- return () => {
38
- isMounted = false;
39
- };
40
- }, [apiRoute]);
7
+ import React from 'react';
41
8
 
9
+ export function MaysonMetadata() {
42
10
  return (
43
- <>
44
- <meta name="mayson-generator" content="Mayson App Platform" />
45
- {stats && (
46
- <meta
47
- name="mayson-realtime-stats"
48
- content={JSON.stringify({
49
- showRemixPill: stats.showRemixPill,
50
- enableRemix: stats.enableRemix,
51
- })}
52
- />
53
- )}
54
- </>
11
+ <meta name="mayson-generator" content="Mayson App Platform" />
55
12
  );
56
13
  }
57
14
  `;
@@ -1,10 +1,13 @@
1
1
  import type { ProjectInjectContext, ProjectInjectResult } from '../../core/types.js';
2
+ /**
3
+ * Generate a purely random 10-character subpath string with no predictable prefix.
4
+ */
2
5
  export declare function generateRandomApiSubpath(): string;
3
6
  export declare function findAppDir(projectRoot: string, layoutPath?: string): string;
4
7
  /**
5
- :// Locate existing mayson API route subpath if present, or generate a new random subpath.
8
+ * Always resolve a fresh purely random API subpath for new injections.
6
9
  */
7
- export declare function resolveMetadataApiSubpath(projectRoot: string, layoutPath?: string, overrideSubpath?: string): string;
10
+ export declare function resolveMetadataApiSubpath(_projectRoot?: string, _layoutPath?: string, overrideSubpath?: string): string;
8
11
  /**
9
12
  * Inject the metadata API route handler (/api/<random-subpath>) into the project workspace.
10
13
  */
@@ -1 +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,wBAAgB,wBAAwB,IAAI,MAAM,CAOjD;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3E;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,CA8BR;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,oBAAoB,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD,mBAAmB,CAiDrB"}
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;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAOjD;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3E;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,EACpB,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,oBAAoB,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD,mBAAmB,CAiDrB"}
@@ -1,13 +1,16 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { generateMetadataApiRouteSource } from './api-handler.js';
4
+ /**
5
+ * Generate a purely random 10-character subpath string with no predictable prefix.
6
+ */
4
7
  export function generateRandomApiSubpath() {
5
8
  const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
6
9
  let randomSlug = '';
7
- for (let i = 0; i < 8; i++) {
10
+ for (let i = 0; i < 10; i++) {
8
11
  randomSlug += chars.charAt(Math.floor(Math.random() * chars.length));
9
12
  }
10
- return `mayson-${randomSlug}`;
13
+ return randomSlug;
11
14
  }
12
15
  export function findAppDir(projectRoot, layoutPath) {
13
16
  if (layoutPath && layoutPath.includes(`${path.sep}src${path.sep}app`)) {
@@ -19,34 +22,12 @@ export function findAppDir(projectRoot, layoutPath) {
19
22
  return path.join(projectRoot, 'app');
20
23
  }
21
24
  /**
22
- :// Locate existing mayson API route subpath if present, or generate a new random subpath.
25
+ * Always resolve a fresh purely random API subpath for new injections.
23
26
  */
24
- export function resolveMetadataApiSubpath(projectRoot, layoutPath, overrideSubpath) {
27
+ export function resolveMetadataApiSubpath(_projectRoot, _layoutPath, overrideSubpath) {
25
28
  if (overrideSubpath && overrideSubpath.trim()) {
26
29
  return overrideSubpath.trim().replace(/^\/api\//, '');
27
30
  }
28
- const appDir = findAppDir(projectRoot, layoutPath);
29
- const apiDir = path.join(appDir, 'api');
30
- if (fs.existsSync(apiDir)) {
31
- try {
32
- const entries = fs.readdirSync(apiDir, { withFileTypes: true });
33
- for (const entry of entries) {
34
- if (entry.isDirectory()) {
35
- // Check if directory starts with mayson- or is randomeapipath
36
- if (entry.name.startsWith('mayson-') || entry.name === 'randomeapipath') {
37
- const hasRoute = fs.existsSync(path.join(apiDir, entry.name, 'route.ts')) ||
38
- fs.existsSync(path.join(apiDir, entry.name, 'route.js'));
39
- if (hasRoute) {
40
- return entry.name;
41
- }
42
- }
43
- }
44
- }
45
- }
46
- catch {
47
- // Ignore reading error
48
- }
49
- }
50
31
  return generateRandomApiSubpath();
51
32
  }
52
33
  /**
@@ -1,4 +1,4 @@
1
1
  import type { PluginGenerateContext } from '../../core/types.js';
2
2
  /** Source for the consumer-app MaysonWatermark client component. */
3
- export declare function buildMaysonWatermarkSource(_context?: PluginGenerateContext): string;
3
+ export declare function buildMaysonWatermarkSource(context?: PluginGenerateContext): string;
4
4
  //# sourceMappingURL=template.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../src/plugins/watermark/template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAKjE,oEAAoE;AACpE,wBAAgB,0BAA0B,CACxC,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,MAAM,CA8GR"}
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../../src/plugins/watermark/template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAKjE,oEAAoE;AACpE,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,CA0JR"}
@@ -1,25 +1,67 @@
1
1
  const LOGO_URL = 'https://mayson.dev/logo/brand-logo-dark.png';
2
2
  const MAYSON_HOME_URL = 'https://mayson.dev/';
3
3
  /** Source for the consumer-app MaysonWatermark client component. */
4
- export function buildMaysonWatermarkSource(_context) {
4
+ export function buildMaysonWatermarkSource(context) {
5
+ const apiRoute = context?.metadata?.apiRoutePath ?? '';
6
+ const appBaseUrl = context?.watermark?.appBaseUrl || MAYSON_HOME_URL;
7
+ const collectionId = context?.watermark?.collectionId || '';
8
+ const initialEnableRemix = context?.watermark?.enableRemix ?? true;
5
9
  return `'use client';
6
10
 
7
- import React, { useState } from 'react';
11
+ import React, { useEffect, useState } from 'react';
8
12
 
9
13
  const LOGO_URL = ${JSON.stringify(LOGO_URL)};
10
- const REMIX_URL = ${JSON.stringify(MAYSON_HOME_URL)};
14
+ const DEFAULT_REMIX_URL = ${JSON.stringify(appBaseUrl)};
15
+ const COLLECTION_ID = ${JSON.stringify(collectionId)};
11
16
 
12
- export function MaysonWatermark() {
17
+ export function MaysonWatermark({ apiRoute = ${JSON.stringify(apiRoute)} }: { apiRoute?: string }) {
13
18
  const [dismissed, setDismissed] = useState(false);
19
+ const [visible, setVisible] = useState(false);
20
+ const [enableRemix, setEnableRemix] = useState(${JSON.stringify(initialEnableRemix)});
14
21
 
15
- if (dismissed) {
22
+ useEffect(() => {
23
+ let isMounted = true;
24
+ async function checkVisibility() {
25
+ if (!apiRoute) {
26
+ setVisible(true);
27
+ return;
28
+ }
29
+ try {
30
+ const res = await fetch(apiRoute, {
31
+ method: 'GET',
32
+ headers: { Accept: 'application/json' },
33
+ });
34
+ if (!res.ok) return;
35
+ const json = await res.json();
36
+ if (json && json.success && json.data && isMounted) {
37
+ if (json.data.showRemixPill === true) {
38
+ setVisible(true);
39
+ } else {
40
+ setVisible(false);
41
+ }
42
+ if (typeof json.data.enableRemix === 'boolean') {
43
+ setEnableRemix(json.data.enableRemix);
44
+ }
45
+ }
46
+ } catch {
47
+ // Silent catch on network error
48
+ }
49
+ }
50
+ checkVisibility();
51
+ return () => {
52
+ isMounted = false;
53
+ };
54
+ }, [apiRoute]);
55
+
56
+ if (dismissed || !visible) {
16
57
  return null;
17
58
  }
18
59
 
19
60
  const handleRemix = (event: React.MouseEvent) => {
20
61
  event.preventDefault();
21
62
  event.stopPropagation();
22
- window.location.href = REMIX_URL;
63
+ if (!enableRemix) return;
64
+ window.location.href = DEFAULT_REMIX_URL;
23
65
  };
24
66
 
25
67
  const handleDismiss = (event: React.MouseEvent) => {
@@ -37,7 +79,7 @@ export function MaysonWatermark() {
37
79
  right: 16,
38
80
  bottom: 16,
39
81
  zIndex: 2147483647,
40
- display: 'inline-flex',
82
+ display: visible ? 'inline-flex' : 'none',
41
83
  alignItems: 'flex-end',
42
84
  gap: 8,
43
85
  padding: '4px 8px',
@@ -58,13 +100,14 @@ export function MaysonWatermark() {
58
100
  gap: 4,
59
101
  color: '#9D9DA2',
60
102
  textDecoration: 'none',
61
- cursor: 'pointer',
103
+ cursor: enableRemix ? 'pointer' : 'default',
62
104
  outline: 'none',
63
105
  border: 'none',
64
106
  background: 'transparent',
65
107
  padding: 0,
66
108
  margin: 0,
67
109
  fontFamily: 'inherit',
110
+ opacity: enableRemix ? 1 : 0.6,
68
111
  }}
69
112
  >
70
113
  <span
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mayson-org/inject-script",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
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",