mirror of
https://github.com/hanzoai/insights-js.git
synced 2026-10-30 18:34:19 +00:00
We own the ingestion server (hanzoai/insights: Django + rust/capture + nodejs plugin) AND this SDK, so there is no external wire contract to preserve. The server is already 100% posthog-free; this SDK was the last laggard. - $last_posthog_reset -> $last_insights_reset. This FIXES A LIVE MISMATCH: the server taxonomy (insights/taxonomy/taxonomy.py, insights/insightsql/ai.py) already expected the insights form while the SDK emitted the posthog form. - __PosthogExtensions__ -> __InsightsExtensions__; all PostHog/posthog/POSTHOG identifiers -> Insights/insights/INSIGHTS (src, tests, playground, playwright, scripts, rrweb subpackages, package.json keywords). - Hosts: DEFAULT_PLUGIN_HOST + DEFAULT_NUXT_HOST + the core host default pointed at PostHog's cloud (us.i.posthog.com) — a real bug. All -> insights.hanzo.ai. - Removed the us-assets/eu-assets CDN split in getRemoteConfig (PostHog-specific; Hanzo serves remote config from the same host) — it had degenerated to a no-op. - Fixed generate-docs writing to a stray references/@hanzo/ subdir. - Renamed the 3 posthog-named test files; dropped 58MB of stale reference snapshots named for upstream posthog versions we never shipped (they are generated output — references/ is now gitignored). Verified: typecheck errors 42 (HEAD) -> 18 (all pre-existing test-mock looseness); types package 0; every package.json valid with @hanzo/* names. Guard: grep -rioE 'posthog' packages ... | wc -l => 0
126 lines
5.4 KiB
TypeScript
126 lines
5.4 KiB
TypeScript
import {
|
|
addInsightsWithBundledScriptsToBundleShellScript,
|
|
disableUserScriptSandboxing,
|
|
modifyExistingXcodeBuildScript,
|
|
} from '../src/tooling/expoconfig'
|
|
|
|
type MockBuildConfig = { buildSettings: Record<string, string> }
|
|
|
|
const mockXcodeProject = (): {
|
|
pbxXCBuildConfigurationSection: () => Record<string, MockBuildConfig>
|
|
configs: Record<string, MockBuildConfig>
|
|
} => {
|
|
const configs: Record<string, MockBuildConfig> = {
|
|
'1A:Release': { buildSettings: { PRODUCT_NAME: '"MyApp"' } },
|
|
'2B:Debug': { buildSettings: { PRODUCT_NAME: '"MyApp"' } },
|
|
'3C:Pods-Release': { buildSettings: { PRODUCT_NAME: '"Pods-MyApp"' } },
|
|
}
|
|
return {
|
|
pbxXCBuildConfigurationSection: () => configs,
|
|
configs,
|
|
}
|
|
}
|
|
|
|
describe('disableUserScriptSandboxing', () => {
|
|
it('sets ENABLE_USER_SCRIPT_SANDBOXING="NO" on every build configuration', () => {
|
|
const xp = mockXcodeProject()
|
|
disableUserScriptSandboxing(xp)
|
|
for (const key of Object.keys(xp.configs)) {
|
|
expect(xp.configs[key].buildSettings.ENABLE_USER_SCRIPT_SANDBOXING).toBe('"NO"')
|
|
}
|
|
})
|
|
|
|
it('uses the literal quoted "NO" string required by the pbxproj format', () => {
|
|
// Unquoted NO corrupts the project file in some xcode-npm versions.
|
|
const xp = mockXcodeProject()
|
|
disableUserScriptSandboxing(xp)
|
|
expect(xp.configs['1A:Release'].buildSettings.ENABLE_USER_SCRIPT_SANDBOXING).not.toBe('NO')
|
|
expect(xp.configs['1A:Release'].buildSettings.ENABLE_USER_SCRIPT_SANDBOXING).not.toBe(false)
|
|
})
|
|
|
|
it('preserves existing build settings', () => {
|
|
const xp = mockXcodeProject()
|
|
disableUserScriptSandboxing(xp)
|
|
expect(xp.configs['1A:Release'].buildSettings.PRODUCT_NAME).toBe('"MyApp"')
|
|
})
|
|
|
|
it('is idempotent — running twice yields the same result', () => {
|
|
const xp = mockXcodeProject()
|
|
disableUserScriptSandboxing(xp)
|
|
disableUserScriptSandboxing(xp)
|
|
expect(xp.configs['1A:Release'].buildSettings.ENABLE_USER_SCRIPT_SANDBOXING).toBe('"NO"')
|
|
})
|
|
})
|
|
|
|
// Extracts the argument that would become $1 inside insights-xcode.sh when
|
|
// the wrapped line is executed by the shell. The shell runs:
|
|
// /bin/sh <insights-xcode.sh-path> <...rest>
|
|
// so $1 is the token immediately after the insights-xcode.sh path.
|
|
const extractArg1 = (wrappedLine: string): string => {
|
|
// The line looks like: /bin/sh `<node eval>` <arg1> ...
|
|
// Split on the backtick-delimited insights-xcode.sh path expression, then
|
|
// take the first whitespace-separated token from whatever follows it.
|
|
const afterInsights = wrappedLine.split(/`[^`]+`/)[1] ?? ''
|
|
return afterInsights.trim().split(/\s+/)[0]
|
|
}
|
|
|
|
describe('addInsightsWithBundledScriptsToBundleShellScript', () => {
|
|
it('wraps the react-native-xcode.sh invocation with insights-xcode.sh', () => {
|
|
const original = '"../node_modules/react-native/scripts/react-native-xcode.sh"'
|
|
const wrapped = addInsightsWithBundledScriptsToBundleShellScript(original)
|
|
expect(wrapped).toContain('insights-xcode.sh')
|
|
expect(wrapped).toContain('react-native-xcode.sh')
|
|
expect(wrapped.startsWith('/bin/sh ')).toBe(true)
|
|
expect(wrapped.indexOf('insights-xcode.sh')).toBeLessThan(wrapped.indexOf('react-native-xcode.sh'))
|
|
})
|
|
|
|
it('supports the alternative packager/ path', () => {
|
|
const original = '"node_modules/react-native/packager/react-native-xcode.sh"'
|
|
const wrapped = addInsightsWithBundledScriptsToBundleShellScript(original)
|
|
expect(wrapped).toContain('insights-xcode.sh')
|
|
expect(wrapped).toContain('packager/react-native-xcode.sh')
|
|
})
|
|
|
|
// Regression tests for issue #3682:
|
|
// When the Expo bundle phase already contains a /bin/sh prefix (common in
|
|
// Expo SDK 53+ and plain RN projects), insights-xcode.sh receives /bin/sh as
|
|
// $1, which makes the REACT_NATIVE_XCODE variable resolve to /bin/sh instead
|
|
// of react-native-xcode.sh, silently breaking the PACKAGER_SOURCEMAP_FILE patch.
|
|
it.each([
|
|
['simple path (no shell prefix)', '../node_modules/react-native/scripts/react-native-xcode.sh'],
|
|
[
|
|
'shell-prefixed command (Expo SDK 53+ / plain RN)',
|
|
'/bin/sh "$PODS_ROOT/../.."/node_modules/react-native/scripts/react-native-xcode.sh',
|
|
],
|
|
])('arg1 passed to insights-xcode.sh is react-native-xcode.sh path, not /bin/sh — %s', (_desc, original) => {
|
|
const wrapped = addInsightsWithBundledScriptsToBundleShellScript(original)
|
|
const arg1 = extractArg1(wrapped)
|
|
expect(arg1).toContain('react-native-xcode.sh')
|
|
expect(arg1).not.toBe('/bin/sh')
|
|
})
|
|
})
|
|
|
|
describe('modifyExistingXcodeBuildScript', () => {
|
|
it('wraps the bundle phase shellScript', () => {
|
|
const script = { shellScript: JSON.stringify('"../node_modules/react-native/scripts/react-native-xcode.sh"') }
|
|
modifyExistingXcodeBuildScript(script)
|
|
const parsed = JSON.parse(script.shellScript)
|
|
expect(parsed).toContain('insights-xcode.sh')
|
|
})
|
|
|
|
it('is idempotent — re-running does not double-wrap', () => {
|
|
const script = { shellScript: JSON.stringify('"../node_modules/react-native/scripts/react-native-xcode.sh"') }
|
|
modifyExistingXcodeBuildScript(script)
|
|
const firstPass = script.shellScript
|
|
modifyExistingXcodeBuildScript(script)
|
|
expect(script.shellScript).toBe(firstPass)
|
|
})
|
|
|
|
it('skips scripts that do not invoke react-native-xcode.sh', () => {
|
|
const script = { shellScript: JSON.stringify('echo "hello"') }
|
|
const original = script.shellScript
|
|
modifyExistingXcodeBuildScript(script)
|
|
expect(script.shellScript).toBe(original)
|
|
})
|
|
})
|