mirror of
https://github.com/stablyai/orca.git
synced 2026-10-04 08:02:09 +00:00
* perf(startup): stop queueing window creation behind the proxy apply and i18n
Three independent, measured startup wins, all free:
1. Park the initial Chromium proxy apply on `mainProcessState` instead of
awaiting it mid-`initializeReadyFoundation`. `setProxy` still starts at the
identical moment; the default-session request guard (which holds, not
cancels) is what actually fences fetchers on it, so only window creation
stops waiting. Runtime launch still awaits it before the desktop relay and
before every headless-serve fetcher.
2. Run `initializeMainProcessI18nAndMenu` concurrently with
`initializeMainProcessRuntimeLaunch`. Nothing in window creation reads a
translated string or the native menu.
3. Load `emojibase-data` in main through `createRequire` on first use instead
of a static import, keeping 166 KB of JSON off `out/main/index.js` and its
~2 ms parse off every launch. The renderer keeps its eager copy unchanged.
out/main/index.js 7,210,071 -> 7,040,147 bytes. No renderer behaviour changes.
* fix(packaging): ship the emoji shortcode dataset main lazily requires
app.asar carries no node_modules, so main's bare requires resolve only out of
Resources/node_modules. emojibase-data is a devDependency and is not in the
packaged runtime allowlist, so the new createRequire in
deferred-emoji-shortcode-dataset.ts threw MODULE_NOT_FOUND in every packaged
build — breaking sanitizeWorktreeName, and with it workspace creation.
Copy the single 166 KB dataset (not the 49 MB package root) into
Resources/node_modules, and gate every createRequire'd bare specifier in
src/main against the packaged resource plan. verifyPackagedMainRuntimeDeps
cannot catch these: the bundler renames the require binding.
* test(proxy): fail CI when a main-process fetcher escapes the default-session guard
The hoist relies on installElectronProxyRequestGuard(session.defaultSession) holding every app-owned request until the persisted proxy lands. Nothing enforced that every fetcher actually lands on defaultSession. Two source-anchored rules do now: no net.fetch/net.request may name a session/partition, and every non-net .fetch( call site is counted against an allowlist.
* test(proxy): close the shorthand and chained-receiver holes in the fetch call-site audit
The audit caught `net.request({ session: x })` and `ident.fetch(`, but not the two
shapes a real regression is just as likely to take: the `{ url, session }` shorthand
that both `net.request` overloads accept, and a receiver with no bare identifier
(`session.fromPartition(...).fetch(`, `ctx.session.fetch(`). Rule 1 now also matches
the shorthand key; rule 2 scans every `.fetch(` and excludes only a literal
`net`/`globalThis`/`global` receiver. Audited counts are unchanged (2/2/1).
* fix(startup): scope the deferred emoji loader to the projects that own it
TS6307: the composite web project lists src/main/ipc/worktree-logic.ts, which
now imports the deferred dataset loader, and the shared lazy test reached into
src/main from a project that has no src/main files. Add the loader to
tsconfig.tc.web.json and move the cross-project case into a src/main test.
Also close the last two review gaps: gate the runtime-RPC startup failure
dialog (the only launch-phase translateMain reader) on a published i18n
barrier so a concurrent i18n phase cannot leave a non-English user with the
English fallback, and let the fetch call-site audit match `net.fetch (url)`.
113 lines
4.0 KiB
TypeScript
113 lines
4.0 KiB
TypeScript
export type StandardEmojiShortcodeEntry = {
|
|
emoji: string
|
|
shortcode: string
|
|
}
|
|
|
|
/** Shape of `emojibase-data/en/shortcodes/emojibase.json`: hexcode -> shortcode or aliases. */
|
|
export type EmojiShortcodeDataset = Readonly<Record<string, string | readonly string[]>>
|
|
|
|
let loadDataset: (() => EmojiShortcodeDataset) | null = null
|
|
|
|
/**
|
|
* Why injected instead of statically imported: the renderer must keep its eager copy (a
|
|
* dynamic import there returned an empty catalog mid-load and persisted `:wink:` literally),
|
|
* but a static import here also inlines the same 166 KB into out/main/index.js and JSON.parses
|
|
* it on every launch. Main supplies a lazy require instead; both stay synchronous.
|
|
*/
|
|
export function setEmojiShortcodeDatasetLoader(load: () => EmojiShortcodeDataset): void {
|
|
loadDataset = load
|
|
}
|
|
|
|
// Skin-tone aliases (`wave_tone3`) are ~40% of the dataset and would drown the suggestion list.
|
|
const SKIN_TONE_SHORTCODE = /_tone\d(?:-\d)?$/
|
|
|
|
type EmojiShortcodeCatalog = {
|
|
entries: readonly StandardEmojiShortcodeEntry[]
|
|
primaryShortcodeByEmoji: ReadonlyMap<string, string>
|
|
segmenter: Intl.Segmenter
|
|
}
|
|
|
|
let catalog: EmojiShortcodeCatalog | null = null
|
|
|
|
// Why lazy: this walks ~3,900 shortcodes and is only needed once a `:` is typed
|
|
// or a worktree name is sanitized, but at module scope every renderer and main
|
|
// boot paid for it. Memoized so the first caller builds it exactly once.
|
|
function loadCatalog(): EmojiShortcodeCatalog {
|
|
if (catalog) {
|
|
return catalog
|
|
}
|
|
if (!loadDataset) {
|
|
throw new Error('Emoji shortcode dataset loader was never registered')
|
|
}
|
|
const grouped = Object.entries(loadDataset()).flatMap(([hexcode, value]) => {
|
|
const shortcodes = (typeof value === 'string' ? [value] : value).filter(
|
|
(shortcode) => !SKIN_TONE_SHORTCODE.test(shortcode)
|
|
)
|
|
return shortcodes.length > 0 ? [{ emoji: hexcodeToEmoji(hexcode), shortcodes }] : []
|
|
})
|
|
catalog = {
|
|
entries: grouped.flatMap(({ emoji, shortcodes }) =>
|
|
shortcodes.map((shortcode) => ({ emoji, shortcode }))
|
|
),
|
|
primaryShortcodeByEmoji: new Map(
|
|
grouped.map(({ emoji, shortcodes }) => [
|
|
normalizeEmojiLookup(emoji),
|
|
primaryShortcode(shortcodes)
|
|
])
|
|
),
|
|
segmenter: new Intl.Segmenter('en', { granularity: 'grapheme' })
|
|
}
|
|
return catalog
|
|
}
|
|
|
|
export function getStandardEmojiShortcodeEntries(): readonly StandardEmojiShortcodeEntry[] {
|
|
return loadCatalog().entries
|
|
}
|
|
|
|
/** Test-only probe for the lazy-boundary guard; never branch on this in product code. */
|
|
export function isEmojiShortcodeCatalogBuiltForTest(): boolean {
|
|
return catalog !== null
|
|
}
|
|
|
|
/**
|
|
* Pick the alias that reads best as a branch or directory name: skip `+1`/`-1` so the name
|
|
* starts with a letter, then cryptic stubs (👎 `no`, ✌ `v`) and the `flag_xx` namespacing
|
|
* prefix, both of which have a spelled-out alias (`thumbsdown`, `victory`, `germany`).
|
|
*/
|
|
function primaryShortcode(shortcodes: readonly string[]): string {
|
|
const named = shortcodes.filter((candidate) => /^[a-z]/i.test(candidate))
|
|
return (
|
|
named.find((candidate) => candidate.length >= 3 && !candidate.startsWith('flag_')) ??
|
|
named.find((candidate) => candidate.length >= 3) ??
|
|
named[0] ??
|
|
shortcodes[0]
|
|
)
|
|
}
|
|
|
|
export function replaceKnownEmojiWithShortcodes(input: string): string {
|
|
const { primaryShortcodeByEmoji, segmenter } = loadCatalog()
|
|
return Array.from(segmenter.segment(input), ({ segment }) => {
|
|
const shortcode = primaryShortcodeByEmoji.get(normalizeEmojiLookup(segment))
|
|
return shortcode ? ` ${shortcode.replaceAll('_', '-')} ` : segment
|
|
}).join('')
|
|
}
|
|
|
|
function normalizeEmojiLookup(emoji: string): string {
|
|
return Array.from(emoji)
|
|
.filter((character) => {
|
|
const codepoint = character.codePointAt(0)
|
|
return (
|
|
character !== '\ufe0f' &&
|
|
(codepoint === undefined || codepoint < 0x1f3fb || codepoint > 0x1f3ff)
|
|
)
|
|
})
|
|
.join('')
|
|
}
|
|
|
|
function hexcodeToEmoji(hexcode: string): string {
|
|
return hexcode
|
|
.split('-')
|
|
.map((codepoint) => String.fromCodePoint(Number.parseInt(codepoint, 16)))
|
|
.join('')
|
|
}
|