Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions packages/realm-server/lib/index-html-injection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import type { DBAdapter } from '@cardstack/runtime-common';
import { query } from '@cardstack/runtime-common';
import {
indexURLCandidates,
indexCandidateExpressions,
} from './index-url-utils';

export async function retrieveHeadHTML({
cardURL,
dbAdapter,
log,
}: {
cardURL: URL;
dbAdapter: DBAdapter;
log?: {
debug: (...args: unknown[]) => void;
};
}): Promise<string | null> {
let candidates = indexURLCandidates(cardURL);

log?.debug(
`Head URL candidates for ${cardURL.href}: ${candidates.join(', ')}`,
);

if (candidates.length === 0) {
log?.debug(`No head candidates for ${cardURL.href}`);
return null;
}

let rows = await query(dbAdapter, [
`
SELECT head_html, realm_version
FROM boxel_index
WHERE type = 'instance'
AND head_html IS NOT NULL
AND is_deleted IS NOT TRUE
AND
`,
...indexCandidateExpressions(candidates),
`
ORDER BY realm_version DESC
LIMIT 1
`,
]);

log?.debug('Head query result for %s', cardURL.href, rows);

let headRow = rows[0] as
| { head_html?: string | null; realm_version?: string | number }
| undefined;

if (headRow?.head_html != null) {
log?.debug(
`Using head HTML from realm version ${headRow.realm_version} for ${cardURL.href}`,
);
} else {
log?.debug(`No head HTML returned from database for ${cardURL.href}`);
}
return headRow?.head_html ?? null;
}

export async function retrieveIsolatedHTML({
cardURL,
dbAdapter,
log,
}: {
cardURL: URL;
dbAdapter: DBAdapter;
log?: {
debug: (...args: unknown[]) => void;
};
}): Promise<string | null> {
let candidates = indexURLCandidates(cardURL);

log?.debug(
`Isolated URL candidates for ${cardURL.href}: ${candidates.join(', ')}`,
);

if (candidates.length === 0) {
log?.debug(`No isolated candidates for ${cardURL.href}`);
return null;
}

let rows = await query(dbAdapter, [
`
SELECT isolated_html, realm_version
FROM boxel_index
WHERE isolated_html IS NOT NULL
AND type = 'instance'
AND is_deleted IS NOT TRUE
AND
`,
...indexCandidateExpressions(candidates),
`
ORDER BY realm_version DESC
LIMIT 1
`,
]);

log?.debug('Isolated query result for %s', cardURL.href, rows);

let isolatedRow = rows[0] as
| { isolated_html?: string | null; realm_version?: string | number }
| undefined;

if (isolatedRow?.isolated_html != null) {
log?.debug(
`Using isolated HTML from realm version ${isolatedRow.realm_version} for ${cardURL.href}`,
);
} else {
log?.debug(`No isolated HTML returned from database for ${cardURL.href}`);
}

return isolatedRow?.isolated_html ?? null;
}

export function injectHeadHTML(indexHTML: string, headHTML: string): string {
return indexHTML.replace(
/(<meta[^>]+data-boxel-head-start[^>]*>)([\s\S]*?)(<meta[^>]+data-boxel-head-end[^>]*>)/,
(_match, start, _content, end) => `${start}\n${headHTML}\n${end}`,
);
}

export function injectIsolatedHTML(
indexHTML: string,
isolatedHTML: string,
): string {
return indexHTML.replace(
/(<script[^>]+id="boxel-isolated-start"[^>]*>\s*<\/script>)([\s\S]*?)(<script[^>]+id="boxel-isolated-end"[^>]*>\s*<\/script>)/,
(_match, start, _content, end) => `${start}\n${isolatedHTML}\n${end}`,
);
}
42 changes: 42 additions & 0 deletions packages/realm-server/lib/index-url-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { param } from '@cardstack/runtime-common';
import {
addExplicitParens,
any,
type Expression,
} from '@cardstack/runtime-common/expression';

export function stripProtocol(href: string): string {
return href.replace(/^https?:\/\//, '');
}

export function indexURLCandidates(cardURL: URL): string[] {
let href = cardURL.href.replace(/\?.*/, '');
let candidates = [href].flatMap((url) => {
// strip trailing slash, but keep root realm URLs that end with slash
let trimmed = url.endsWith('/') ? url.slice(0, -1) : url;
let withIndex = url.endsWith('/') ? `${trimmed}/index` : `${url}/index`;
let withJson = `${url.replace(/\/?$/, '')}.json`;
let withIndexJson = `${withIndex}.json`;
return [url, trimmed, withIndex, withJson, withIndexJson];
});

return [...new Set(candidates)];
}

export function indexCandidateExpressions(candidates: string[]): Expression {
// Proxying means the apparent request URL will be http but in the database it's https
return addExplicitParens(
any(
candidates.flatMap((candidate) => [
[
"regexp_replace(url, '^https?://', '') =",
param(stripProtocol(candidate)),
],
[
"regexp_replace(file_alias, '^https?://', '') =",
param(stripProtocol(candidate)),
],
]),
) as Expression,
) as Expression;
}
8 changes: 4 additions & 4 deletions packages/realm-server/lib/retrieve-scoped-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import { expressionToSql, query } from '@cardstack/runtime-common';
import { parseDeps } from '@cardstack/runtime-common/realm';
import type { Expression } from '@cardstack/runtime-common/expression';
import { decodeScopedCSSRequest, isScopedCSSRequest } from 'glimmer-scoped-css';
import {
indexURLCandidates,
indexCandidateExpressions,
} from './index-url-utils';

export async function retrieveScopedCSS({
cardURL,
dbAdapter,
indexURLCandidates,
indexCandidateExpressions,
log,
}: {
cardURL: URL;
dbAdapter: DBAdapter;
indexURLCandidates: (cardURL: URL) => string[];
indexCandidateExpressions: (candidates: string[]) => Expression;
log?: {
debug: (...args: unknown[]) => void;
trace: (...args: unknown[]) => void;
Expand Down
Loading