@assembly-js/app-foundation 0.2.7 → 0.3.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/CHANGELOG.md +3 -0
- package/bin/foundation-exports.mjs +1 -1
- package/dist/bridge-vnext.d.ts +14 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.js +2 -0
- package/dist/location-prefix.d.ts +19 -0
- package/dist/location-prefix.js +60 -0
- package/dist/location-reporter.d.ts +1 -0
- package/dist/location-reporter.js +46 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -19,6 +19,9 @@ A patch release never changes what an app's build requires; a minor release may
|
|
|
19
19
|
not by hand; see `changes/README.md`. CI fails a PR that changes a check script without a
|
|
20
20
|
`bump: minor` change file.
|
|
21
21
|
|
|
22
|
+
## 0.3.0
|
|
23
|
+
- [new] `LocationReporter` is exported from `@assembly-js/app-foundation/client`, so apps born before the deeplink image can mount it in their layout and report their inner location to the host with the same surface-prefix handling as new apps.
|
|
24
|
+
|
|
22
25
|
## 0.2.7
|
|
23
26
|
- [guidance] The build agent learns to call another app through its generated client and to show an empty state naming that app when the call is refused.
|
|
24
27
|
|
|
@@ -27,7 +27,7 @@ export const NON_CLIENT_EXPORTS = new Set([
|
|
|
27
27
|
'ConnectionStatus', 'ConnectorError', 'ContractDocumentError', 'ConnectorErrorCode', 'CookieOutcome', 'CronHandler',
|
|
28
28
|
'DEFAULT_LABELS', 'EXCHANGE_REJECTION_HEADER', 'ExchangeRejectedViewer', 'ExchangedSession',
|
|
29
29
|
'FormatAirtableRecordsOpts', 'GenerateAppClientInput', 'GeneratedAppClient', 'GmailSendArgs', 'GmailSendResult', 'IntegrationClient',
|
|
30
|
-
'InternalUserAccess', 'LEGACY_APP_IDS', 'MintFailure', 'MintOutcome', 'MintSessionOptions', 'MintedAppBearer',
|
|
30
|
+
'InternalUserAccess', 'LEGACY_APP_IDS', 'LocationReporter', 'MintFailure', 'MintOutcome', 'MintSessionOptions', 'MintedAppBearer',
|
|
31
31
|
'MintedBearer', 'NETWORK_CAPTURE_MESSAGE_TYPE', 'NetworkCaptureHeaderFingerprint',
|
|
32
32
|
'NetworkCaptureRecord', 'PAINTED_BEACON_EVENT', 'PAINTED_BEACON_PATH',
|
|
33
33
|
'PORTAL_ORIGIN_COOKIE_NAME', 'PdfText', 'PrincipalAllow', 'PrincipalKind',
|
package/dist/bridge-vnext.d.ts
CHANGED
|
@@ -71,3 +71,17 @@ declare module '@assembly-js/app-bridge' {
|
|
|
71
71
|
navigateTo(destination: NavigateDestination, options?: NavigateToOptions): Promise<NavigateResult>;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
export type LocationNavigatePayload = {
|
|
75
|
+
path: string;
|
|
76
|
+
search: string;
|
|
77
|
+
replace?: boolean;
|
|
78
|
+
};
|
|
79
|
+
export interface LocationBridge {
|
|
80
|
+
report(path: string, search: string, replace?: boolean): void;
|
|
81
|
+
onNavigate(handler: (payload: LocationNavigatePayload) => void): void;
|
|
82
|
+
}
|
|
83
|
+
declare module '@assembly-js/app-bridge' {
|
|
84
|
+
interface AssemblyBridgeApi {
|
|
85
|
+
location: LocationBridge;
|
|
86
|
+
}
|
|
87
|
+
}
|
package/dist/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -23,3 +23,5 @@ export * from './theme.js';
|
|
|
23
23
|
// at compile time, but a value import from ./server would trip the server-only
|
|
24
24
|
// guard for no reason.
|
|
25
25
|
export * from './resource-resolution.js';
|
|
26
|
+
// Browser-safe: the reporter pulls in no Node built-ins, unlike ./next.
|
|
27
|
+
export { LocationReporter } from './location-reporter.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare function getSurfacePrefixes(): string[];
|
|
2
|
+
export declare const SURFACE_PREFIXES: string[];
|
|
3
|
+
/**
|
|
4
|
+
* Detect which surface prefix the given pathname belongs to.
|
|
5
|
+
* Returns the prefix string (e.g. '/c') or '' for the internal surface.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getSurfacePrefix(pathname: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Remove a surface prefix from a pathname, returning an app-local path.
|
|
10
|
+
* '/c/orders/42' with prefix '/c' → '/orders/42'
|
|
11
|
+
* '/c' with prefix '/c' → '/'
|
|
12
|
+
*/
|
|
13
|
+
export declare function stripPrefix(pathname: string, prefix: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Re-attach a surface prefix to an app-local path received from the host.
|
|
16
|
+
* '/orders/42' with prefix '/c' → '/c/orders/42'
|
|
17
|
+
* '/' with prefix '/c' → '/c'
|
|
18
|
+
*/
|
|
19
|
+
export declare function addPrefix(prefix: string, path: string): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const DEFAULT_PREFIXES = ['/c', '/n'];
|
|
2
|
+
// Read surface prefixes from the build-time env value injected by withAssemblyDev.
|
|
3
|
+
// Falls back to the historical /c and /n defaults so apps on an older image
|
|
4
|
+
// continue to work without a rebuild.
|
|
5
|
+
export function getSurfacePrefixes() {
|
|
6
|
+
const raw = process.env.NEXT_PUBLIC_ASSEMBLY_SURFACE_PREFIXES;
|
|
7
|
+
if (!raw)
|
|
8
|
+
return DEFAULT_PREFIXES;
|
|
9
|
+
try {
|
|
10
|
+
const parsed = JSON.parse(raw);
|
|
11
|
+
const prefixes = parsed
|
|
12
|
+
.filter((p) => typeof p === 'string' && p !== '/')
|
|
13
|
+
.map((p) => p.replace(/\/+$/, ''));
|
|
14
|
+
if (prefixes.length === 0)
|
|
15
|
+
return DEFAULT_PREFIXES;
|
|
16
|
+
// Longest first so /clients never matches as /c + "lients".
|
|
17
|
+
return [...prefixes].sort((a, b) => b.length - a.length);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return DEFAULT_PREFIXES;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Kept for backwards compatibility; callers that import this directly still work.
|
|
24
|
+
export const SURFACE_PREFIXES = getSurfacePrefixes();
|
|
25
|
+
/**
|
|
26
|
+
* Detect which surface prefix the given pathname belongs to.
|
|
27
|
+
* Returns the prefix string (e.g. '/c') or '' for the internal surface.
|
|
28
|
+
*/
|
|
29
|
+
export function getSurfacePrefix(pathname) {
|
|
30
|
+
for (const prefix of getSurfacePrefixes()) {
|
|
31
|
+
if (pathname === prefix || pathname.startsWith(`${prefix}/`)) {
|
|
32
|
+
return prefix;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Remove a surface prefix from a pathname, returning an app-local path.
|
|
39
|
+
* '/c/orders/42' with prefix '/c' → '/orders/42'
|
|
40
|
+
* '/c' with prefix '/c' → '/'
|
|
41
|
+
*/
|
|
42
|
+
export function stripPrefix(pathname, prefix) {
|
|
43
|
+
if (!prefix)
|
|
44
|
+
return pathname;
|
|
45
|
+
if (pathname === prefix)
|
|
46
|
+
return '/';
|
|
47
|
+
return pathname.slice(prefix.length) || '/';
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Re-attach a surface prefix to an app-local path received from the host.
|
|
51
|
+
* '/orders/42' with prefix '/c' → '/c/orders/42'
|
|
52
|
+
* '/' with prefix '/c' → '/c'
|
|
53
|
+
*/
|
|
54
|
+
export function addPrefix(prefix, path) {
|
|
55
|
+
if (!prefix)
|
|
56
|
+
return path;
|
|
57
|
+
if (path === '/')
|
|
58
|
+
return prefix;
|
|
59
|
+
return `${prefix}${path}`;
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function LocationReporter(): import("react").FunctionComponentElement<import("react").SuspenseProps>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Suspense, createElement, useEffect, useRef } from 'react';
|
|
3
|
+
import { usePathname, useSearchParams, useRouter } from 'next/navigation';
|
|
4
|
+
import { AssemblyBridge } from '@assembly-js/app-bridge';
|
|
5
|
+
import { getSurfacePrefix, stripPrefix, addPrefix } from './location-prefix.js';
|
|
6
|
+
function LocationReporterInner() {
|
|
7
|
+
const pathname = usePathname();
|
|
8
|
+
const searchParams = useSearchParams();
|
|
9
|
+
const router = useRouter();
|
|
10
|
+
// Mutable ref so the onNavigate closure always sees the current prefix
|
|
11
|
+
// without needing to re-register the handler on every pathname change.
|
|
12
|
+
const prefixRef = useRef('');
|
|
13
|
+
const prefix = getSurfacePrefix(pathname);
|
|
14
|
+
prefixRef.current = prefix;
|
|
15
|
+
// Bridges below 2.1 type `location` (see bridge-vnext.ts) but lack it at runtime.
|
|
16
|
+
const bridgeLocation = AssemblyBridge.location;
|
|
17
|
+
// Report location on every client-side route change.
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!bridgeLocation)
|
|
20
|
+
return;
|
|
21
|
+
const path = stripPrefix(pathname, prefix);
|
|
22
|
+
const qs = searchParams.toString();
|
|
23
|
+
bridgeLocation.report(path, qs ? `?${qs}` : '');
|
|
24
|
+
}, [bridgeLocation, pathname, searchParams, prefix]);
|
|
25
|
+
// Register the navigate handler once; rely on prefixRef for the current prefix.
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!bridgeLocation)
|
|
28
|
+
return;
|
|
29
|
+
bridgeLocation.onNavigate(({ path, search, replace }) => {
|
|
30
|
+
const dest = addPrefix(prefixRef.current, path) + (search ?? '');
|
|
31
|
+
if (replace) {
|
|
32
|
+
router.replace(dest);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
router.push(dest);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}, [bridgeLocation, router]);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
// useSearchParams requires a Suspense boundary to avoid de-opting the route
|
|
42
|
+
// to full client-side rendering in Next.js App Router. createElement keeps
|
|
43
|
+
// the package free of a jsx compiler setting.
|
|
44
|
+
export function LocationReporter() {
|
|
45
|
+
return createElement(Suspense, { fallback: null }, createElement(LocationReporterInner));
|
|
46
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assembly-js/app-foundation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Versioned foundation helpers and build context for Assembly generated apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"@neondatabase/serverless": "^1.1.0",
|
|
57
57
|
"@types/bun": "^1.1.0",
|
|
58
58
|
"@types/node": "^22",
|
|
59
|
+
"@types/react": "^19.3.0",
|
|
59
60
|
"drizzle-orm": "^0.45.2",
|
|
60
61
|
"esbuild": "^0.25.12",
|
|
61
62
|
"next": "^15.5.0",
|