@keepsnappy/react-native 0.1.0-alpha.12 → 0.1.0-alpha.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app.plugin.js CHANGED
@@ -1,6 +1,6 @@
1
- // Expo config plugin for the PerfLab SDK.
1
+ // Expo config plugin for the KeepSnappy SDK.
2
2
  //
3
- // The native module (iOS pod PerfLab, Android expo.modules.perflab) is linked
3
+ // The native module (iOS pod KeepSnappy, Android expo.modules.keepsnappy) is linked
4
4
  // by Expo autolinking from expo-module.config.json. With the build flag off
5
5
  // (EXPO_PUBLIC_KEEPSNAPPY is not "1" when `expo prebuild` runs) this plugin
6
6
  // excludes the package from Expo autolinking, so a store build carries no SDK
@@ -20,8 +20,7 @@ const { withPodfile, withSettingsGradle } = require('expo/config-plugins');
20
20
  const PACKAGE_NAME = require('./package.json').name;
21
21
  const PACKAGE_NAMES = [...new Set([PACKAGE_NAME, '@keepsnappy/react-native'])];
22
22
  const namesFor = name => name === PACKAGE_NAME ? PACKAGE_NAMES : [name];
23
- // Keep this generated-line marker stable so old exclusions can be removed.
24
- const TAG = 'perflab: native module excluded, EXPO_PUBLIC_PERF_LAB is not 1';
23
+ const TAG = 'keepsnappy: native module excluded, EXPO_PUBLIC_KEEPSNAPPY is not 1';
25
24
 
26
25
  const isEnabled = () => (process.env.EXPO_PUBLIC_KEEPSNAPPY) === '1';
27
26
 
@@ -61,7 +60,9 @@ const gradleLine = (language, name) =>
61
60
  : `expoAutolinking.exclude = (expoAutolinking.exclude ?: []) + [${namesFor(name).map(quoted).join(', ')}] // ${TAG}`;
62
61
 
63
62
  const editSettingsGradle = (contents, enabled, language = 'groovy', name = PACKAGE_NAME) => {
64
- const lines = contents.split('\n').filter(line => !line.includes(TAG));
63
+ // Match the generated statement independently of its branding comment.
64
+ const generated = ['groovy', 'kt'].map(dsl => gradleLine(dsl, name).split(' // ')[0]);
65
+ const lines = contents.split('\n').filter(line => !generated.includes(line.split(' // ')[0].trim()));
65
66
  if (enabled) {
66
67
  return lines.join('\n');
67
68
  }
@@ -81,7 +82,7 @@ const withKeepSnappy = config => {
81
82
  if (enabled) {
82
83
  config.extra = {
83
84
  ...config.extra,
84
- perflab: { native: true, profilingRenderer: profilingRenderer() },
85
+ keepsnappy: { native: true, profilingRenderer: profilingRenderer() },
85
86
  };
86
87
  }
87
88
  config = withPodfile(config, mod => {
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "platforms": ["ios", "android"],
3
3
  "ios": {
4
- "modules": ["PerfLabModule"]
4
+ "modules": ["KeepSnappyModule"]
5
5
  },
6
6
  "android": {
7
- "modules": ["expo.modules.perflab.PerfLabModule"]
7
+ "modules": ["expo.modules.keepsnappy.KeepSnappyModule"]
8
8
  }
9
9
  }
@@ -3,7 +3,7 @@ require 'json'
3
3
  package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
4
 
5
5
  Pod::Spec.new do |s|
6
- s.name = 'PerfLab'
6
+ s.name = 'KeepSnappy'
7
7
  s.version = package['version']
8
8
  s.summary = package['description']
9
9
  s.description = package['description']
@@ -19,7 +19,7 @@ Pod::Spec.new do |s|
19
19
  s.dependency 'hermes-engine'
20
20
 
21
21
  s.source_files = "*.{swift,h,mm}"
22
- s.public_header_files = "PerfLabHermes.h"
22
+ s.public_header_files = "KeepSnappyHermes.h"
23
23
 
24
24
  s.pod_target_xcconfig = {
25
25
  'DEFINES_MODULE' => 'YES',
@@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN
4
4
 
5
5
  // Hermes sampling profiler (C++ API) for Swift. The runtime pointer is the
6
6
  // jsi::Runtime behind Expo's JavaScriptRuntime.withUnsafePointee.
7
- @interface PerfLabHermes : NSObject
7
+ @interface KeepSnappyHermes : NSObject
8
8
 
9
9
  + (BOOL)startWithHz:(double)hz;
10
10
  + (BOOL)stop;
@@ -1,4 +1,4 @@
1
- #import "PerfLabHermes.h"
1
+ #import "KeepSnappyHermes.h"
2
2
 
3
3
  #include <fstream>
4
4
  #include <sstream>
@@ -13,7 +13,7 @@ static fh::IHermesRootAPI *rootAPI() {
13
13
  return jsi::castInterface<fh::IHermesRootAPI>(fh::makeHermesRootAPI());
14
14
  }
15
15
 
16
- @implementation PerfLabHermes
16
+ @implementation KeepSnappyHermes
17
17
 
18
18
  + (BOOL)startWithHz:(double)hz {
19
19
  auto *api = rootAPI();
@@ -4,38 +4,38 @@ import Foundation
4
4
  import UIKit
5
5
  import os
6
6
 
7
- private let pointsOfInterest = OSLog(subsystem: "com.gigaverse.perflab", category: .pointsOfInterest)
8
- private let logger = OSLog(subsystem: "com.gigaverse.perflab", category: "log")
7
+ private let pointsOfInterest = OSLog(subsystem: "com.gigaverse.keepsnappy", category: .pointsOfInterest)
8
+ private let logger = OSLog(subsystem: "com.gigaverse.keepsnappy", category: "log")
9
9
 
10
10
  // os_log truncates long messages, so long payloads go out in chunks.
11
11
  private let logChunk = 800
12
12
 
13
13
  // Created by Expo at app start whether or not the SDK measures: nothing here may
14
14
  // do work before a function is called (a dormant build calls activationRead only).
15
- public class PerfLabModule: Module {
16
- private lazy var logQueue = DispatchQueue(label: "com.gigaverse.perflab.log")
15
+ public class KeepSnappyModule: Module {
16
+ private lazy var logQueue = DispatchQueue(label: "com.gigaverse.keepsnappy.log")
17
17
  private var logHandle: FileHandle?
18
18
  private var spoolHandle: FileHandle?
19
19
  private var samplerCreated = false
20
- private lazy var sampler: PerfLabSampler = {
20
+ private lazy var sampler: KeepSnappySampler = {
21
21
  self.samplerCreated = true
22
- return PerfLabSampler { [weak self] line in self?.writeLog(line) }
22
+ return KeepSnappySampler { [weak self] line in self?.writeLog(line) }
23
23
  }()
24
24
 
25
25
  public func definition() -> ModuleDefinition {
26
- Name("PerfLab")
26
+ Name("KeepSnappy")
27
27
 
28
28
  // Signpost names must be static, so the window name travels in the message.
29
29
  Function("beginAsyncSection") { (name: String, cookie: Int) in
30
- os_signpost(.begin, log: pointsOfInterest, name: "perf-lab", signpostID: OSSignpostID(UInt64(cookie)), "%{public}s", name)
30
+ os_signpost(.begin, log: pointsOfInterest, name: "keepsnappy", signpostID: OSSignpostID(UInt64(cookie)), "%{public}s", name)
31
31
  }
32
32
 
33
33
  Function("endAsyncSection") { (name: String, cookie: Int) in
34
- os_signpost(.end, log: pointsOfInterest, name: "perf-lab", signpostID: OSSignpostID(UInt64(cookie)), "%{public}s", name)
34
+ os_signpost(.end, log: pointsOfInterest, name: "keepsnappy", signpostID: OSSignpostID(UInt64(cookie)), "%{public}s", name)
35
35
  }
36
36
 
37
37
  Function("setCounter") { (name: String, value: Double) in
38
- os_signpost(.event, log: pointsOfInterest, name: "perf-lab-counter", "%{public}s=%f", name, value)
38
+ os_signpost(.event, log: pointsOfInterest, name: "keepsnappy-counter", "%{public}s=%f", name, value)
39
39
  }
40
40
 
41
41
  Function("isTracing") { () -> Bool in
@@ -67,7 +67,7 @@ public class PerfLabModule: Module {
67
67
  }
68
68
 
69
69
  Function("samplerNow") { () -> Double in
70
- perfLabMonotonicMs()
70
+ keepSnappyMonotonicMs()
71
71
  }
72
72
 
73
73
  OnDestroy {
@@ -98,7 +98,7 @@ public class PerfLabModule: Module {
98
98
  Self.emit(message)
99
99
  }
100
100
 
101
- // Push journal (perf-lab/push-spool.jsonl): append is queued, read and replace wait for
101
+ // Push journal (keepsnappy/push-spool.jsonl): append is queued, read and replace wait for
102
102
  // the queue so they see every earlier append.
103
103
  Function("spoolAppend") { (record: String) in
104
104
  self.logQueue.async {
@@ -117,18 +117,18 @@ public class PerfLabModule: Module {
117
117
  }
118
118
 
119
119
  // Result of a `flush=1` link for the test driver: a transparent 1x1 pt element at the
120
- // top-left of the key window, accessibility id `perflab.flushed`, label ok / error / off.
120
+ // top-left of the key window, accessibility id `keepsnappy.flushed`, label ok / error / off.
121
121
  // Only ever created on request in a measuring process; replaced by the next flush.
122
122
  Function("flushSignal") { (status: String, detail: String) in
123
123
  Task { @MainActor in
124
- Self.showSignal(tag: Self.flushSignalTag, id: "perflab.flushed", label: status, value: detail)
124
+ Self.showSignal(tag: Self.flushSignalTag, id: "keepsnappy.flushed", label: status, value: detail)
125
125
  }
126
126
  }
127
127
 
128
- // The answer to a driver command (GC before the action): the same kind of element, id `perflab.ack`, label `gc:<n> ok`.
128
+ // The answer to a driver command (GC before the action): the same kind of element, id `keepsnappy.ack`, label `gc:<n> ok`.
129
129
  Function("ackSignal") { (text: String) in
130
130
  Task { @MainActor in
131
- Self.showSignal(tag: Self.ackSignalTag, id: "perflab.ack", label: text, value: text)
131
+ Self.showSignal(tag: Self.ackSignalTag, id: "keepsnappy.ack", label: text, value: text)
132
132
  }
133
133
  }
134
134
 
@@ -139,6 +139,31 @@ public class PerfLabModule: Module {
139
139
  }
140
140
  }
141
141
 
142
+ // The profile files a flush has not uploaded yet, with their run tokens; read by a later process.
143
+ Function("artifactsPendingRead") { () -> String? in
144
+ self.logQueue.sync {
145
+ guard let file = Self.fileIfPresent("artifacts-pending.json") else { return nil }
146
+ return try? String(contentsOf: file, encoding: .utf8)
147
+ }
148
+ }
149
+
150
+ Function("artifactsPendingWrite") { (content: String) -> Bool in
151
+ self.logQueue.sync {
152
+ guard let dir = Self.outputDir() else { return false }
153
+ let file = dir.appendingPathComponent("artifacts-pending.json")
154
+ if content.isEmpty {
155
+ try? FileManager.default.removeItem(at: file)
156
+ return true
157
+ }
158
+ do {
159
+ try content.write(to: file, atomically: true, encoding: .utf8)
160
+ return true
161
+ } catch {
162
+ return false
163
+ }
164
+ }
165
+ }
166
+
142
167
  Function("spoolReplace") { (content: String) -> Bool in
143
168
  self.logQueue.sync {
144
169
  try? self.spoolHandle?.close()
@@ -175,7 +200,7 @@ public class PerfLabModule: Module {
175
200
  ]
176
201
  }
177
202
 
178
- // Documents/perf-lab is pulled with `xcrun devicectl device copy from --domain-type appDataContainer`.
203
+ // Documents/keepsnappy is pulled with `xcrun devicectl device copy from --domain-type appDataContainer`.
179
204
  Function("writeText") { (fileName: String, content: String) -> String? in
180
205
  guard let dir = Self.outputDir() else { return nil }
181
206
  let file = dir.appendingPathComponent(fileName.replacingOccurrences(of: "/", with: "_"))
@@ -187,14 +212,14 @@ public class PerfLabModule: Module {
187
212
  }
188
213
  }
189
214
 
190
- // Hermes sampling profiler (PerfLabHermes.mm). Sync functions run on the JS
215
+ // Hermes sampling profiler (KeepSnappyHermes.mm). Sync functions run on the JS
191
216
  // thread, which the per-runtime dump needs.
192
217
  Function("cpuProfileStart") { (hz: Double) -> Bool in
193
- PerfLabHermes.start(withHz: hz)
218
+ KeepSnappyHermes.start(withHz: hz)
194
219
  }
195
220
 
196
221
  Function("cpuProfileStop") { () -> Bool in
197
- PerfLabHermes.stop()
222
+ KeepSnappyHermes.stop()
198
223
  }
199
224
 
200
225
  // A raw profile file of a run (plan 40 §3.6): gzip, sha256 of the gzip, PUT to the
@@ -245,10 +270,10 @@ public class PerfLabModule: Module {
245
270
  guard let dir = Self.outputDir() else { return nil }
246
271
  let path = dir.appendingPathComponent(fileName.replacingOccurrences(of: "/", with: "_")).path
247
272
  guard let runtime = try? self.appContext?.runtime else {
248
- return PerfLabHermes.dumpRuntime(nil, toPath: path)
273
+ return KeepSnappyHermes.dumpRuntime(nil, toPath: path)
249
274
  }
250
275
  return runtime.withUnsafePointee { pointer in
251
- PerfLabHermes.dumpRuntime(pointer, toPath: path)
276
+ KeepSnappyHermes.dumpRuntime(pointer, toPath: path)
252
277
  }
253
278
  }
254
279
  }
@@ -258,7 +283,7 @@ public class PerfLabModule: Module {
258
283
 
259
284
  private static func outputDir() -> URL? {
260
285
  guard let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
261
- let dir = docs.appendingPathComponent("perf-lab", isDirectory: true)
286
+ let dir = docs.appendingPathComponent("keepsnappy", isDirectory: true)
262
287
  try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
263
288
  return dir
264
289
  }
@@ -296,12 +321,26 @@ public class PerfLabModule: Module {
296
321
  private static let flushSignalTag = 0x9E7F_1A5
297
322
  private static let ackSignalTag = 0x9E7F_1A6
298
323
 
324
+ // No window yet (a relaunch, a link mid-transition): tried again this often, this far apart.
325
+ private static let signalRetries = 3
326
+ private static let signalRetryNanoseconds: UInt64 = 1_000_000_000
327
+
299
328
  @MainActor
300
- private static func showSignal(tag: Int, id: String, label status: String, value detail: String) {
329
+ private static func showSignal(tag: Int, id: String, label status: String, value detail: String, attempt: Int = 0) {
301
330
  let windows = UIApplication.shared.connectedScenes
302
331
  .compactMap { $0 as? UIWindowScene }
303
332
  .flatMap { $0.windows }
304
- guard let window = windows.first(where: { $0.isKeyWindow }) ?? windows.first else { return }
333
+ guard let window = windows.first(where: { $0.isKeyWindow }) ?? windows.first else {
334
+ if attempt < signalRetries {
335
+ Task { @MainActor in
336
+ try? await Task.sleep(nanoseconds: signalRetryNanoseconds)
337
+ Self.showSignal(tag: tag, id: id, label: status, value: detail, attempt: attempt + 1)
338
+ }
339
+ } else {
340
+ emit("KEEPSNAPPY \(id) not shown: no window after \(attempt + 1) tries (\(status))")
341
+ }
342
+ return
343
+ }
305
344
  window.viewWithTag(tag)?.removeFromSuperview()
306
345
  let label = UILabel(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
307
346
  label.tag = tag
@@ -334,7 +373,7 @@ public class PerfLabModule: Module {
334
373
 
335
374
  private static func fileIfPresent(_ name: String) -> URL? {
336
375
  guard let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
337
- let file = docs.appendingPathComponent("perf-lab", isDirectory: true).appendingPathComponent(name)
376
+ let file = docs.appendingPathComponent("keepsnappy", isDirectory: true).appendingPathComponent(name)
338
377
  return FileManager.default.fileExists(atPath: file.path) ? file : nil
339
378
  }
340
379
 
@@ -19,7 +19,7 @@ private let timebase: mach_timebase_info_data_t = {
19
19
  }()
20
20
 
21
21
  // performance.now() in Hermes on iOS is steady_clock = mach_absolute_time.
22
- func perfLabMonotonicMs() -> Double {
22
+ func keepSnappyMonotonicMs() -> Double {
23
23
  Double(mach_absolute_time()) * Double(timebase.numer) / Double(timebase.denom) / 1_000_000
24
24
  }
25
25
 
@@ -82,7 +82,7 @@ private func memoryMb() -> (footprint: Double, resident: Double)? {
82
82
  // on ProMotion screens the link runs at 60 Hz while duration reports the 120 Hz budget.
83
83
  // The link asks for no frame rate, so it never raises the screen's refresh rate; a single
84
84
  // 120 Hz drop between two 60 Hz callbacks is invisible.
85
- final class PerfLabFrameCounter: NSObject {
85
+ final class KeepSnappyFrameCounter: NSObject {
86
86
  private var link: CADisplayLink?
87
87
  private var lastTimestamp: CFTimeInterval = 0
88
88
  private var lastBudget: CFTimeInterval = 0
@@ -176,11 +176,11 @@ final class PerfLabFrameCounter: NSObject {
176
176
 
177
177
  // Off unless configured. `auto` samples on a utility queue every intervalMs and needs no JS;
178
178
  // `manual` only keeps the frame counter running and samples when asked. Every sample is logged
179
- // as `PERF_LAB sample {json}` through the module's native log and kept in a short ring for windows.
180
- final class PerfLabSampler {
181
- private let queue = DispatchQueue(label: "com.gigaverse.perflab.sampler", qos: .utility)
179
+ // as `KEEPSNAPPY sample {json}` through the module's native log and kept in a short ring for windows.
180
+ final class KeepSnappySampler {
181
+ private let queue = DispatchQueue(label: "com.gigaverse.keepsnappy.sampler", qos: .utility)
182
182
  private let log: (String) -> Void
183
- private let frames = PerfLabFrameCounter()
183
+ private let frames = KeepSnappyFrameCounter()
184
184
 
185
185
  private var timer: DispatchSourceTimer?
186
186
  private var mode = "off"
@@ -231,7 +231,7 @@ final class PerfLabSampler {
231
231
  timer = source
232
232
  }
233
233
  }
234
- return ["mode": mode, "intervalMs": intervalMs, "nowMs": perfLabMonotonicMs()]
234
+ return ["mode": mode, "intervalMs": intervalMs, "nowMs": keepSnappyMonotonicMs()]
235
235
  }
236
236
  }
237
237
 
@@ -300,7 +300,7 @@ final class PerfLabSampler {
300
300
  }
301
301
 
302
302
  private func collect(reason: String) -> [String: Any] {
303
- let started = perfLabMonotonicMs()
303
+ let started = keepSnappyMonotonicMs()
304
304
  let t = started
305
305
  let processMs = processCpuMs()
306
306
  let main = frames.mainThread
@@ -349,7 +349,7 @@ final class PerfLabSampler {
349
349
  previous = (t, processMs, mainMs, jsMs, frame.presentedTotal, frame.missedTotal)
350
350
  record["cpu"] = cpu
351
351
  record["frames"] = framesOut
352
- record["collectMs"] = perfLabMonotonicMs() - started
352
+ record["collectMs"] = keepSnappyMonotonicMs() - started
353
353
 
354
354
  ring.append(record)
355
355
  if ring.count > ringSize {
@@ -357,7 +357,7 @@ final class PerfLabSampler {
357
357
  }
358
358
  if let data = try? JSONSerialization.data(withJSONObject: record),
359
359
  let json = String(data: data, encoding: .utf8) {
360
- log("PERF_LAB sample \(json)")
360
+ log("KEEPSNAPPY sample \(json)")
361
361
  }
362
362
  return record
363
363
  }
@@ -1,5 +1,5 @@
1
1
  // Wraps the app's Babel transformer (set by withKeepSnappy) and adds one import to
2
- // the entry module: the perf-lab boot module. That import is what puts the SDK
2
+ // the entry module: the keepsnappy boot module. That import is what puts the SDK
3
3
  // in the graph; getModulesRunBeforeMainModule then runs it before the entry.
4
4
  //
5
5
  // babel-preset-expo inlines EXPO_PUBLIC_* only outside node_modules, and the
@@ -11,8 +11,9 @@ const path = require('path');
11
11
  const upstreamPath = (process.env.KEEPSNAPPY_UPSTREAM_BABEL_TRANSFORMER);
12
12
  const entryFile = (process.env.KEEPSNAPPY_ENTRY_FILE);
13
13
  const bootFile = (process.env.KEEPSNAPPY_BOOT_FILE);
14
+ const sdkVersion = process.env.KEEPSNAPPY_SDK_VERSION;
14
15
 
15
- if (!upstreamPath || !entryFile || !bootFile) {
16
+ if (!upstreamPath || !entryFile || !bootFile || !sdkVersion) {
16
17
  throw new Error(
17
18
  'KeepSnappy Babel transformer loaded without withKeepSnappy env (KEEPSNAPPY_*)',
18
19
  );
@@ -57,7 +58,10 @@ module.exports = {
57
58
  return upstream.transform({ ...args, src: bootImport() + args.src });
58
59
  }
59
60
  if (file.startsWith(packageRoot)) {
60
- return upstream.transform({ ...args, src: inlineEnv(args.src) });
61
+ const src = file === path.join(packageRoot, 'js', 'sdk', 'push-install.ts')
62
+ ? args.src.replace("require('../../package.json')", JSON.stringify({ version: sdkVersion }))
63
+ : args.src;
64
+ return upstream.transform({ ...args, src: inlineEnv(src) });
61
65
  }
62
66
  return upstream.transform(args);
63
67
  },
@@ -66,6 +70,6 @@ module.exports = {
66
70
  typeof upstream.getCacheKey === 'function'
67
71
  ? upstream.getCacheKey(...args)
68
72
  : '';
69
- return `${base}$perflab:${entryFile}:${bootFile}:${envSignature()}`;
73
+ return `${base}$keepsnappy:${entryFile}:${bootFile}:${sdkVersion}:${envSignature()}`;
70
74
  },
71
75
  };
@@ -0,0 +1,23 @@
1
+ // Build tooling only: Metro workers receive the identity in their inherited env.
2
+ const { execFileSync } = require('child_process');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function sdkBuildVersion() {
7
+ const packageRoot = path.resolve(__dirname, '..');
8
+ const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
9
+ if (!pkg.private) return pkg.version;
10
+ const git = args => execFileSync('git', args, { cwd: packageRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
11
+ try {
12
+ const root = git(['rev-parse', '--show-toplevel']);
13
+ const release = JSON.parse(fs.readFileSync(path.join(root, 'release.json'), 'utf8')).version;
14
+ if (typeof release !== 'string' || !release) throw new Error('release.json has no version');
15
+ const sha = git(['rev-parse', '--short', 'HEAD']);
16
+ const dirty = git(['status', '--porcelain', '--untracked-files=normal']) ? '.dirty' : '';
17
+ return `${release}+local.${sha}${dirty}`;
18
+ } catch (cause) {
19
+ throw new Error('KeepSnappy: cannot identify this unpublished SDK; build from its Git checkout or use a staged release package', { cause });
20
+ }
21
+ }
22
+
23
+ module.exports = { sdkBuildVersion };
@@ -1,4 +1,4 @@
1
- // perf-lab React DevTools hook, installed as a Metro polyfill: it runs before
1
+ // keepsnappy React DevTools hook, installed as a Metro polyfill: it runs before
2
2
  // any module, so it exists before the React renderer evaluates and injects.
3
3
  // A polyfill is a plain script (no require), so this file stays self-contained:
4
4
  // it only counts and buffers. The SDK boot module attaches a listener later.
@@ -10,11 +10,11 @@
10
10
  // module cannot be reached yet, the hook is prepared but not installed and the
11
11
  // boot module decides (it still runs before the renderer loads).
12
12
  //
13
- // `profile` on __PERFLAB_ACTIVATION__ picks the React renderer in a
13
+ // `profile` on __KEEPSNAPPY_ACTIVATION__ picks the React renderer in a
14
14
  // KEEPSNAPPY_REACT_PROFILING=switch bundle (js/renderer-switch.js): the
15
15
  // activation's `profile` in a dormant build, EXPO_PUBLIC_KEEPSNAPPY_PROFILE otherwise.
16
16
  (function (g) {
17
- if (g.__PERFLAB_HOOK__ || g.__PERFLAB_ACTIVATION__) {
17
+ if (g.__KEEPSNAPPY_HOOK__ || g.__KEEPSNAPPY_ACTIVATION__) {
18
18
  return;
19
19
  }
20
20
  var HOOK = '__REACT_DEVTOOLS_GLOBAL_HOOK__';
@@ -85,7 +85,7 @@
85
85
  }
86
86
  }
87
87
  var modules = g.expo && g.expo.modules;
88
- var native = modules ? modules.PerfLab : undefined;
88
+ var native = modules ? modules.KeepSnappy : undefined;
89
89
  if (native && typeof native.activationRead === 'function') {
90
90
  activation.raw = native.activationRead();
91
91
  activation.read = 'polyfill';
@@ -99,7 +99,7 @@
99
99
  activation.read = 'deferred';
100
100
  }
101
101
  }
102
- g.__PERFLAB_ACTIVATION__ = activation;
102
+ g.__KEEPSNAPPY_ACTIVATION__ = activation;
103
103
  if (MODE === 'dormant' && activation.read === 'polyfill' && !activation.active) {
104
104
  return;
105
105
  }
@@ -135,7 +135,7 @@
135
135
  var log = function (message) {
136
136
  try {
137
137
  if (g.console && typeof g.console.log === 'function') {
138
- g.console.log('PERF_LAB ' + message);
138
+ g.console.log('KEEPSNAPPY ' + message);
139
139
  }
140
140
  } catch (_e) {
141
141
  return;
@@ -220,7 +220,7 @@
220
220
  supportsFiber: true,
221
221
  isDisabled: false,
222
222
  renderers: new Map(),
223
- __perflab: true,
223
+ __keepsnappy: true,
224
224
  inject: function (internals) {
225
225
  var id = nextId++;
226
226
  ownHook.renderers.set(id, internals);
@@ -241,7 +241,7 @@
241
241
  // Wrap a hook someone else installed (React DevTools, bippy, ...): theirs runs
242
242
  // first, ours after, and the renderer id stays theirs.
243
243
  var chain = function (hook) {
244
- if (!hook || hook.__perflab || hook.__perflabChained) {
244
+ if (!hook || hook.__keepsnappy || hook.__keepsnappyChained) {
245
245
  return;
246
246
  }
247
247
  var origInject = hook.inject;
@@ -267,7 +267,7 @@
267
267
  recordInject(id, internals);
268
268
  });
269
269
  }
270
- hook.__perflabChained = true;
270
+ hook.__keepsnappyChained = true;
271
271
  state.chainedTargets++;
272
272
  state.mode = 'chained';
273
273
  };
@@ -301,7 +301,7 @@
301
301
  var existing = g[HOOK];
302
302
  if (existing === undefined || existing === null) {
303
303
  installOwn();
304
- } else if (!existing.__perflab) {
304
+ } else if (!existing.__keepsnappy) {
305
305
  chain(existing);
306
306
  }
307
307
  return state.mode;
@@ -314,7 +314,7 @@
314
314
  ensure();
315
315
  }
316
316
 
317
- g.__PERFLAB_HOOK__ = {
317
+ g.__KEEPSNAPPY_HOOK__ = {
318
318
  state: state,
319
319
  ensure: ensure,
320
320
  attach: function (listener) {
@@ -5,7 +5,7 @@
5
5
  // resolves the two module names below to RN's own implementation files.
6
6
  'use strict';
7
7
 
8
- var activation = globalThis.__PERFLAB_ACTIVATION__;
8
+ var activation = globalThis.__KEEPSNAPPY_ACTIVATION__;
9
9
  var profiling = !!activation && activation.profile === true;
10
10
  if (activation) {
11
11
  activation.renderer = profiling ? 'profiling' : 'prod';
@@ -6,29 +6,29 @@
6
6
  // manual - markers only until a settings link turns collection on (no mode set)
7
7
  // dormant - nothing installed until an activation link; it applies from the NEXT launch
8
8
  //
9
- // The activation record lives in the app sandbox (`perf-lab/activation.json`,
9
+ // The activation record lives in the app sandbox (`keepsnappy/activation.json`,
10
10
  // written by the native module). hook.polyfill.js reads it once, synchronously,
11
- // before React loads, and leaves the raw value in __PERFLAB_ACTIVATION__.
11
+ // before React loads, and leaves the raw value in __KEEPSNAPPY_ACTIVATION__.
12
12
  // Its `profile` field picks the React renderer in a KEEPSNAPPY_REACT_PROFILING=switch
13
13
  // bundle (js/renderer-switch.js), so a process is wholly one renderer.
14
14
 
15
- export const PERF_LAB_ENABLED = (process.env.EXPO_PUBLIC_KEEPSNAPPY) === '1';
15
+ export const KEEPSNAPPY_ENABLED = (process.env.EXPO_PUBLIC_KEEPSNAPPY) === '1';
16
16
 
17
- export const PERF_LAB_DEEP_LINK_PATH = 'devtools/perf-lab';
17
+ export const KEEPSNAPPY_DEEP_LINK_PATH = 'devtools/keepsnappy';
18
18
 
19
- export type PerfLabMode = 'auto' | 'manual' | 'dormant';
19
+ export type KeepSnappyMode = 'auto' | 'manual' | 'dormant';
20
20
 
21
21
  export const resolveMode = (
22
22
  mode: string | undefined,
23
23
  auto: string | undefined,
24
- ): PerfLabMode => {
24
+ ): KeepSnappyMode => {
25
25
  if (mode === 'dormant' || mode === 'auto') {
26
26
  return mode;
27
27
  }
28
28
  return auto === '1' ? 'auto' : 'manual';
29
29
  };
30
30
 
31
- export const MODE: PerfLabMode = resolveMode(
31
+ export const MODE: KeepSnappyMode = resolveMode(
32
32
  (process.env.EXPO_PUBLIC_KEEPSNAPPY_MODE),
33
33
  (process.env.EXPO_PUBLIC_KEEPSNAPPY_AUTO),
34
34
  );
@@ -53,7 +53,7 @@ export interface ActivationRecord {
53
53
  export type ActivationRead = 'polyfill' | 'boot' | 'unavailable' | 'none';
54
54
 
55
55
  export interface Decision {
56
- mode: PerfLabMode;
56
+ mode: KeepSnappyMode;
57
57
  active: boolean;
58
58
  runToken: string | null;
59
59
  expiresAt: number | null;
@@ -135,7 +135,7 @@ const OFF: ActivationRecord = {
135
135
 
136
136
  // Must agree with activeRecord() in hook.polyfill.js (tested on the same fixtures).
137
137
  export const decide = (
138
- mode: PerfLabMode,
138
+ mode: KeepSnappyMode,
139
139
  record: ActivationRecord | null,
140
140
  nowMs: number,
141
141
  read: ActivationRead,
@@ -252,8 +252,8 @@ export const parseQuery = (
252
252
  export const queryOf = (url: string) => parseQuery(url.split('?')[1]);
253
253
 
254
254
  interface ActivationGlobal {
255
- __PERFLAB_ACTIVATION__?: PolyfillActivation;
256
- __PERFLAB_HOOK__?: { ensure: () => string };
255
+ __KEEPSNAPPY_ACTIVATION__?: PolyfillActivation;
256
+ __KEEPSNAPPY_HOOK__?: { ensure: () => string };
257
257
  expo?: { modules?: Record<string, unknown> };
258
258
  }
259
259
 
@@ -266,7 +266,7 @@ const isNative = (value: unknown): value is ActivationNative =>
266
266
 
267
267
  // Found lazily: only links and a deferred startup read need it.
268
268
  export const findActivationNative = (): ActivationNative | null => {
269
- const direct = g.expo?.modules?.PerfLab;
269
+ const direct = g.expo?.modules?.KeepSnappy;
270
270
  if (isNative(direct)) {
271
271
  return direct;
272
272
  }
@@ -278,7 +278,7 @@ export const findActivationNative = (): ActivationNative | null => {
278
278
  const core = require('expo-modules-core') as {
279
279
  requireOptionalNativeModule?: (name: string) => unknown;
280
280
  };
281
- const found = core.requireOptionalNativeModule?.('PerfLab');
281
+ const found = core.requireOptionalNativeModule?.('KeepSnappy');
282
282
  return isNative(found) ? found : null;
283
283
  } catch {
284
284
  return null;
@@ -290,7 +290,7 @@ export const resolveDecision = (
290
290
  nowMs: number = Date.now(),
291
291
  findNative: () => ActivationNative | null = findActivationNative,
292
292
  ): Decision => {
293
- const fromPolyfill = g.__PERFLAB_ACTIVATION__;
293
+ const fromPolyfill = g.__KEEPSNAPPY_ACTIVATION__;
294
294
  if (MODE !== 'dormant') {
295
295
  // Not gated: the record only carries a persisted token, read when it is cheap.
296
296
  if (fromPolyfill && fromPolyfill.read === 'polyfill') {
@@ -324,7 +324,7 @@ export const resolveDecision = (
324
324
  decision = decide(MODE, null, nowMs, 'unavailable');
325
325
  }
326
326
  if (decision.active) {
327
- g.__PERFLAB_HOOK__?.ensure();
327
+ g.__KEEPSNAPPY_HOOK__?.ensure();
328
328
  }
329
329
  // The polyfill could not choose the renderer; boot still runs before it loads.
330
330
  if (fromPolyfill && fromPolyfill.read === 'deferred') {
package/js/sdk/active.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  applySettings,
8
8
  attachHook,
9
9
  exposeGlobal,
10
- handlePerfLabDeepLink,
10
+ handleKeepSnappyDeepLink,
11
11
  logStartup,
12
12
  perfLog,
13
13
  setActivation,
@@ -25,7 +25,7 @@ const guarded = (label: string, run: () => void) => {
25
25
  run();
26
26
  } catch (error) {
27
27
  globalThis.console.log(
28
- `PERF_LAB boot step failed ${label} ${String(error)}`,
28
+ `KEEPSNAPPY boot step failed ${label} ${String(error)}`,
29
29
  );
30
30
  }
31
31
  };
@@ -41,7 +41,7 @@ export const startActive = (decision: Decision) => {
41
41
  installLinks(
42
42
  (url, via) => {
43
43
  perfLog(`link via=${via} ${redactUrl(url)}`);
44
- handlePerfLabDeepLink(url);
44
+ handleKeepSnappyDeepLink(url);
45
45
  },
46
46
  noteAppLink,
47
47
  ),