Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for matching internal middlewares #79

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
35 changes: 19 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@
},
"peerDependencies": {
"next": "^13.0 || ^14.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
"dependencies": {
"@croct/content": "^1.0.0",
"@croct/plug": "^0.16.2",
"@croct/plug-react": "^0.9.0",
"@croct/sdk": "^0.17.4",
"cookie": "^0.7.0",
"uuid": "^10.0.0"
"path-to-regexp": "^8.2.0"
},
"devDependencies": {
"@babel/core": "^7.20.2",
Expand Down
3 changes: 3 additions & 0 deletions src/config/appId.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const PATTERN = /^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/;

/**
* @internal
*/
export function getAppId(): string {
const appId = process.env.NEXT_PUBLIC_CROCT_APP_ID ?? '';

Expand Down
5 changes: 3 additions & 2 deletions src/config/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ describe('getRequestContext', () => {

it('should throw an error when the client ID is missing', () => {
expect(() => getRequestContext(new Headers(), createCookieJar())).toThrow(
'Croct\'s Client ID is missing. Did you forget to configure Croct\'s middleware? '
+ 'For help, see: https://croct.help/sdk/nextjs/missing-middleware',
'Croct\'s Client ID is missing. Did you configure Croct\'s middleware? '
+ 'If you\'re using a `matcher`, ensure it includes all page routes. '
+ 'For help, see: https://croct.help/sdk/nextjs/missing-middleware',
);
});

Expand Down
14 changes: 12 additions & 2 deletions src/config/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,24 @@ export type RequestContext = {
preferredLocale?: string,
};

/**
* @internal
*/
export function resolveRequestContext(route?: RouteContext): RequestContext {
return getRequestContext(getHeaders(route), getCookies(route));
}

/**
* @internal
*/
export function getRequestContext(headers: HeaderReader, cookies: CookieReader): RequestContext {
const clientId = headers.get(Header.CLIENT_ID);

if (clientId === null) {
throw new Error(
'Croct\'s Client ID is missing. Did you forget to configure Croct\'s middleware? '
+ 'For help, see: https://croct.help/sdk/nextjs/missing-middleware',
'Croct\'s Client ID is missing. Did you configure Croct\'s middleware? '
+ 'If you\'re using a `matcher`, ensure it includes all page routes. '
+ 'For help, see: https://croct.help/sdk/nextjs/missing-middleware',
);
}

Expand Down Expand Up @@ -78,6 +85,9 @@ export function getRequestContext(headers: HeaderReader, cookies: CookieReader):
return context;
}

/**
* @internal
*/
export function resolvePreferredLocale(route?: RouteContext): string|null {
return getPreferredLocale(getHeaders(route));
}
Expand Down
9 changes: 9 additions & 0 deletions src/config/cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export type CookieOptions = {
httpOnly?: boolean,
};

/**
* @internal
*/
export function getClientIdCookieOptions(): CookieOptions {
const duration = normalizeValue(process.env.NEXT_PUBLIC_CROCT_CLIENT_ID_COOKIE_DURATION, `${365 * 24 * 60 * 60}`);
const parsedDuration = Number.parseInt(duration, 10);
Expand All @@ -30,6 +33,9 @@ export function getClientIdCookieOptions(): CookieOptions {
};
}

/**
* @internal
*/
export function getUserTokenCookieOptions(): CookieOptions {
const duration = normalizeValue(process.env.NEXT_PUBLIC_CROCT_USER_TOKEN_COOKIE_DURATION, `${7 * 24 * 60 * 60}`);
const parsedDuration = Number.parseInt(duration, 10);
Expand All @@ -52,6 +58,9 @@ export function getUserTokenCookieOptions(): CookieOptions {
};
}

/**
* @internal
*/
export function getPreviewCookieOptions(): CookieOptions {
const domain = normalizeValue(process.env.NEXT_PUBLIC_CROCT_PREVIEW_TOKEN_COOKIE_DOMAIN, '');

Expand Down
12 changes: 12 additions & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ export function getEnvValue(value: string|undefined): string|undefined;

export function getEnvValue<V extends string|undefined, R>(value: V, normalize: Normalizer<V, R>): R;

/**
* @internal
*/
export function getEnvValue<V extends string|undefined, R>(
value: V,
normalize?: Normalizer<V, R>,
Expand All @@ -15,6 +18,9 @@ export function getEnvValue<V extends string|undefined, R>(
return normalize !== undefined ? normalize(value) : value;
}

/**
* @internal
*/
export function getEnvFlag(value: string|undefined): boolean {
return getEnvValue(value, flag => flag === 'true');
}
Expand All @@ -27,6 +33,9 @@ export function getEnvEntry<K extends string, V extends string|undefined, R>(
normalize: Normalizer<V, R>,
): Record<K, R>|undefined;

/**
* @internal
*/
export function getEnvEntry<K extends string, V extends string|undefined, R>(
key: K,
value: V,
Expand All @@ -39,6 +48,9 @@ export function getEnvEntry<K extends string, V extends string|undefined, R>(
return {[key]: normalize !== undefined ? normalize(value) : value};
}

/**
* @internal
*/
export function getEnvEntryFlag(key: string, value: string|undefined): Record<string, boolean>|undefined {
return getEnvEntry(key, value, flag => flag === 'true');
}
18 changes: 16 additions & 2 deletions src/config/security.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {ApiKey} from '@croct/sdk/apiKey';
import {Token} from '@croct/sdk/token';
import {v4 as uuid} from 'uuid';
import {getAppId} from '@/config/appId';

/**
* @internal
*/
export function getApiKey(): ApiKey {
const apiKey = process.env.CROCT_API_KEY;

Expand All @@ -21,6 +23,9 @@ export function getApiKey(): ApiKey {
}
}

/**
* @internal
*/
export function getAuthenticationKey(): ApiKey {
const apiKey = getApiKey();

Expand All @@ -35,11 +40,17 @@ export function getAuthenticationKey(): ApiKey {
return apiKey;
}

/**
* @internal
*/
export function isUserTokenAuthenticationEnabled(): boolean {
return process.env.CROCT_API_KEY !== undefined
&& process.env.CROCT_DISABLE_USER_TOKEN_AUTHENTICATION !== 'true';
}

/**
* @internal
*/
export function getTokenDuration(): number {
const duration = process.env.CROCT_TOKEN_DURATION;

Expand All @@ -59,12 +70,15 @@ export function getTokenDuration(): number {
return parsedDuration;
}

/**
* @internal
*/
export function issueToken(userId: string|null = null): Promise<Token> {
const token = Token.issue(getAppId(), userId)
.withDuration(getTokenDuration());

if (isUserTokenAuthenticationEnabled()) {
return token.withTokenId(uuid())
return token.withTokenId(crypto.randomUUID())
.signedWith(getAuthenticationKey());
}

Expand Down
3 changes: 3 additions & 0 deletions src/config/timeout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const DEFAULT_FETCH_TIMEOUT = 2000;

/**
* @internal
*/
export function getDefaultFetchTimeout(): number | undefined {
const timeout = process.env.NEXT_PUBLIC_CROCT_DEFAULT_FETCH_TIMEOUT;

Expand Down
15 changes: 15 additions & 0 deletions src/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export type RouteContext = {
res: PartialResponse,
};

/**
* @internal
*/
export function getHeaders(route?: RouteContext): HeaderReader {
try {
const {headers} = importNextHeaders();
Expand Down Expand Up @@ -65,6 +68,9 @@ export function getHeaders(route?: RouteContext): HeaderReader {
};
}

/**
* @internal
*/
export function getCookies(route?: RouteContext): CookieAccessor {
try {
const {cookies} = importNextHeaders();
Expand Down Expand Up @@ -151,14 +157,23 @@ export function getCookies(route?: RouteContext): CookieAccessor {
};
}

/**
* @internal
*/
function isNextRequestHeaders(headers: PartialRequest['headers']): headers is NextRequest['headers'] {
return headers.append !== undefined;
}

/**
* @internal
*/
function isNextRequestCookies(cookies: PartialRequest['cookies']): cookies is NextRequest['cookies'] {
return cookies.get !== undefined || cookies.set !== undefined;
}

/**
* @internal
*/
export function isAppRouter(): boolean {
try {
const {headers} = importNextHeaders();
Expand Down
Loading
Loading