Skip to content

Commit 17e4385

Browse files
committed
update readme
1 parent 68171d5 commit 17e4385

File tree

9 files changed

+52
-44
lines changed

9 files changed

+52
-44
lines changed

.changeset/eight-adults-type.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@segment/analytics-signals': minor
3+
---
4+
5+
Add globalscope strategy

packages/signals/signals-example/src/lib/analytics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const isStage = process.env.STAGE === 'true'
3333

3434
const signalsPlugin = new SignalsPlugin({
3535
...(isStage ? { apiHost: 'signals.segment.build/v1' } : {}),
36-
// enableDebugLogging: true,
36+
sandboxStrategy: 'global',
3737
// processSignal: processSignalExample,
3838
})
3939

packages/signals/signals/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ signalsPlugin.addSignal({ someData: 'foo' })
8686
}
8787
```
8888

89+
### Sandbox Strategies
90+
If getting CSP errors, you can use the experimental 'global' sandbox strategy.
91+
92+
```ts
93+
new SignalsPlugin({ sandboxStrategy: 'global' })
94+
```
95+
96+
8997
### Debugging
9098
Debug mode **MUST** be enabled on the client to VIEW signals on segment.com.
9199

packages/signals/signals/src/core/middleware/event-processor/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import { Signal } from '@segment/analytics-signals-runtime'
2+
import { logger } from '../../../lib/logger'
23
import { SignalBuffer } from '../../buffer'
34
import { SignalsSubscriber, SignalsMiddlewareContext } from '../../emitter'
45
import { SignalEventProcessor } from '../../processor/processor'
56
import {
67
normalizeEdgeFunctionURL,
78
GlobalScopeSandbox,
89
WorkerSandbox,
9-
WorkerSandboxSettings,
10+
IframeSandboxSettings,
1011
SignalSandbox,
1112
NoopSandbox,
1213
} from '../../processor/sandbox'
1314

14-
const GLOBAL_SCOPE_SANDBOX = true
15-
1615
export class SignalsEventProcessorSubscriber implements SignalsSubscriber {
1716
processor!: SignalEventProcessor
1817
buffer!: SignalBuffer
@@ -25,19 +24,26 @@ export class SignalsEventProcessorSubscriber implements SignalsSubscriber {
2524
)
2625

2726
let sandbox: SignalSandbox
27+
console.log('sup')
2828
if (!normalizedEdgeFunctionURL) {
2929
console.warn(
3030
`No processSignal function found. Have you written a processSignal function on app.segment.com?`
3131
)
32+
logger.debug('Initializing sandbox: noop')
3233
sandbox = new NoopSandbox()
33-
} else if (!GLOBAL_SCOPE_SANDBOX || sandboxSettings.processSignal) {
34+
} else if (
35+
sandboxSettings.sandboxStrategy === 'iframe' ||
36+
sandboxSettings.processSignal
37+
) {
38+
logger.debug('Initializing sandbox: iframe')
3439
sandbox = new WorkerSandbox(
35-
new WorkerSandboxSettings({
40+
new IframeSandboxSettings({
3641
processSignal: sandboxSettings.processSignal,
3742
edgeFnDownloadURL: normalizedEdgeFunctionURL,
3843
})
3944
)
4045
} else {
46+
logger.debug('Initializing sandbox: global scope')
4147
sandbox = new GlobalScopeSandbox({
4248
edgeFnDownloadURL: normalizedEdgeFunctionURL,
4349
})

packages/signals/signals/src/core/processor/__tests__/sandbox-settings.test.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { WorkerSandboxSettings, SandboxSettingsConfig } from '../sandbox'
1+
import { IframeSandboxSettings, IframeSandboxSettingsConfig } from '../sandbox'
22

3-
describe(WorkerSandboxSettings, () => {
3+
describe(IframeSandboxSettings, () => {
44
const edgeFnResponseBody = `function processSignal() { console.log('hello world') }`
5-
const baseSettings: SandboxSettingsConfig = {
6-
functionHost: undefined,
5+
const baseSettings: IframeSandboxSettingsConfig = {
76
processSignal: undefined,
87
edgeFnDownloadURL: 'http://example.com/download',
98
edgeFnFetchClient: jest.fn().mockReturnValue(
@@ -13,21 +12,21 @@ describe(WorkerSandboxSettings, () => {
1312
),
1413
}
1514
test('initializes with provided settings', async () => {
16-
const sandboxSettings = new WorkerSandboxSettings({ ...baseSettings })
15+
const sandboxSettings = new IframeSandboxSettings({ ...baseSettings })
1716
expect(baseSettings.edgeFnFetchClient).toHaveBeenCalledWith(
1817
baseSettings.edgeFnDownloadURL
1918
)
2019
expect(await sandboxSettings.processSignal).toEqual(edgeFnResponseBody)
2120
})
2221

2322
test('normalizes edgeFnDownloadURL when functionHost is provided', async () => {
24-
const settings: SandboxSettingsConfig = {
23+
const settings = {
2524
...baseSettings,
2625
processSignal: undefined,
2726
functionHost: 'newHost.com',
2827
edgeFnDownloadURL: 'https://original.com/download',
2928
}
30-
new WorkerSandboxSettings(settings)
29+
new IframeSandboxSettings(settings)
3130
expect(baseSettings.edgeFnFetchClient).toHaveBeenCalledWith(
3231
'https://newHost.com/download'
3332
)
@@ -37,12 +36,12 @@ describe(WorkerSandboxSettings, () => {
3736
const consoleWarnSpy = jest
3837
.spyOn(console, 'warn')
3938
.mockImplementation(() => {})
40-
const settings: SandboxSettingsConfig = {
39+
const settings = {
4140
...baseSettings,
4241
processSignal: undefined,
4342
edgeFnDownloadURL: undefined,
4443
}
45-
const sandboxSettings = new WorkerSandboxSettings(settings)
44+
const sandboxSettings = new IframeSandboxSettings(settings)
4645
expect(await sandboxSettings.processSignal).toEqual(
4746
'globalThis.processSignal = function processSignal() {}'
4847
)

packages/signals/signals/src/core/processor/sandbox.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,21 +172,15 @@ export type SandboxSettingsConfig = {
172172
processSignal: string | undefined
173173
edgeFnDownloadURL: string | undefined
174174
edgeFnFetchClient?: typeof fetch
175-
/**
176-
* What sandbox strategy to use
177-
* default - use a web worker and regular evaluation
178-
* globalScope - evaluate everything in the global scope -- this avoids CSP errors
179-
* @default 'default'
180-
*/
181-
sandboxStrategy?: 'default' | 'globalScope'
175+
sandboxStrategy: 'iframe' | 'global'
182176
}
183177

184-
export type WorkerboxSettingsConfig = Pick<
178+
export type IframeSandboxSettingsConfig = Pick<
185179
SandboxSettingsConfig,
186180
'processSignal' | 'edgeFnFetchClient' | 'edgeFnDownloadURL'
187181
>
188182

189-
export class WorkerSandboxSettings {
183+
export class IframeSandboxSettings {
190184
/**
191185
* Should look like:
192186
* ```js
@@ -196,7 +190,7 @@ export class WorkerSandboxSettings {
196190
* ```
197191
*/
198192
processSignal: Promise<string>
199-
constructor(settings: WorkerboxSettingsConfig) {
193+
constructor(settings: IframeSandboxSettingsConfig) {
200194
const fetch = settings.edgeFnFetchClient ?? globalThis.fetch
201195

202196
const processSignalNormalized = settings.processSignal
@@ -218,10 +212,10 @@ export interface SignalSandbox {
218212
}
219213

220214
export class WorkerSandbox implements SignalSandbox {
221-
settings: WorkerSandboxSettings
215+
settings: IframeSandboxSettings
222216
jsSandbox: CodeSandbox
223217

224-
constructor(settings: WorkerSandboxSettings) {
218+
constructor(settings: IframeSandboxSettings) {
225219
this.settings = settings
226220
this.jsSandbox = new JavascriptSandbox()
227221
}
@@ -280,11 +274,9 @@ const processWithGlobalScopeExecutionEnv = (
280274
const originalAnalytics = g.analytics
281275
try {
282276
if (g['analytics'] instanceof AnalyticsRuntime) {
283-
console.warn(
277+
throw new Error(
284278
'Invariant: analytics variable was not properly restored on the previous execution. This indicates a concurrency bug'
285279
)
286-
287-
return
288280
}
289281

290282
g['analytics'] = analytics
@@ -319,6 +311,7 @@ export class GlobalScopeSandbox implements SignalSandbox {
319311
htmlScriptLoaded: Promise<HTMLScriptElement>
320312

321313
constructor(settings: GlobalScopeSandboxSettings) {
314+
logger.debug('Initializing global scope sandbox')
322315
this.htmlScriptLoaded = loadScript(settings.edgeFnDownloadURL)
323316
}
324317

packages/signals/signals/src/core/signals/settings.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type SignalsSettingsConfig = Pick<
2828
| 'mutationGenPollInterval'
2929
| 'mutationGenObservedAttributes'
3030
| 'debug'
31+
| 'sandboxStrategy'
3132
> & {
3233
signalStorage?: SignalPersistentStorage
3334
processSignal?: string
@@ -89,6 +90,7 @@ export class SignalGlobalSettings {
8990
},
9091
}
9192
this.sandbox = {
93+
sandboxStrategy: settings.sandboxStrategy ?? 'iframe',
9294
functionHost: settings.functionHost,
9395
processSignal: settings.processSignal,
9496
edgeFnDownloadURL: undefined,

packages/signals/signals/src/plugin/signals-plugin.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,11 @@ export class SignalsPlugin implements Plugin, SignalsAugmentedFunctionality {
3434
Object.assign(window, { SegmentSignalsPlugin: this })
3535

3636
this.signals = new Signals({
37-
debug: settings.debug,
38-
disableSignalsRedaction: settings.disableSignalsRedaction,
39-
enableSignalsIngestion: settings.enableSignalsIngestion,
40-
flushAt: settings.flushAt,
41-
flushInterval: settings.flushInterval,
42-
functionHost: settings.functionHost,
43-
apiHost: settings.apiHost,
44-
maxBufferSize: settings.maxBufferSize,
37+
...settings,
4538
processSignal:
4639
typeof settings.processSignal === 'function'
4740
? settings.processSignal.toString()
4841
: settings.processSignal,
49-
networkSignalsAllowSameDomain: settings.networkSignalsAllowSameDomain,
50-
networkSignalsAllowList: settings.networkSignalsAllowList,
51-
networkSignalsDisallowList: settings.networkSignalsDisallowList,
52-
signalStorage: settings.signalStorage,
53-
signalStorageType: settings.signalStorageType,
54-
middleware: settings.middleware,
5542
})
5643

5744
logger.debug(`SignalsPlugin v${version} initializing`, {

packages/signals/signals/src/types/settings.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ export interface SignalsPluginSettingsConfig {
144144
* (defaultAttributes) => defaultAttributes.filter(attr => attr.toLowerCase() !== 'aria-selected')
145145
*/
146146
mutationGenObservedAttributes?: (defaultAttributes: string[]) => string[]
147+
148+
/**
149+
* What sandbox strategy to use
150+
* - global - [EXPERIMENTAL] evaluate everything in the global scope -- use this if you want to avoid CSP errors.
151+
* - iframe - use a web worker and regular evaluation
152+
* @default 'iframe'
153+
*/
154+
sandboxStrategy?: 'iframe' | 'global'
147155
}
148156

149157
export type RegexLike = RegExp | string

0 commit comments

Comments
 (0)