@seekprotocol/game 0.1.2 → 0.1.3

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/bridge.d.ts CHANGED
@@ -49,6 +49,7 @@ export interface FinishResult {
49
49
  export interface Bridge {
50
50
  ready(): Promise<LaunchInfo>;
51
51
  launch(): LaunchInfo;
52
+ t(key: string, vars?: Record<string, string | number>): string;
52
53
  finish(result: {
53
54
  score: number;
54
55
  seconds: number;
package/index.js CHANGED
@@ -312,6 +312,26 @@ var TRACK_MAX_STRING = 64;
312
312
  var MAX_TOKEN_LENGTH = 4096;
313
313
  var PROXY_ORIGIN = "https://api.play.seekprotocol.ai";
314
314
  var DEFAULT_HOST_ORIGINS = ["https://storeconnect.seekprotocol.ai"];
315
+ function readGameTexts(v) {
316
+ if (!isObj2(v))
317
+ return {};
318
+ const out = {};
319
+ let n = 0;
320
+ for (const [k, x] of Object.entries(v)) {
321
+ if (n >= 300)
322
+ break;
323
+ if (typeof x === "string" && x.length <= 2000 && /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,59}$/.test(k)) {
324
+ out[k] = x;
325
+ n++;
326
+ }
327
+ }
328
+ return out;
329
+ }
330
+ function fillText(text, vars) {
331
+ if (!vars)
332
+ return text;
333
+ return text.replace(/\{([A-Za-z][A-Za-z0-9_]{0,30})\}/g, (m, name) => Object.prototype.hasOwnProperty.call(vars, name) ? String(vars[name]) : m);
334
+ }
315
335
  var utf8Bytes = (s) => new TextEncoder().encode(s).byteLength;
316
336
  function jsonBytes(value) {
317
337
  try {
@@ -482,12 +502,13 @@ function decodeLaunchToken(token) {
482
502
  }
483
503
  if (!isObj2(c))
484
504
  return null;
485
- const { run, seed, lang, sub, game, version, exp } = c;
505
+ const { run, seed, lang, sub, game, version, exp, variant, step } = c;
486
506
  if (typeof run !== "string" || typeof sub !== "string" || typeof game !== "string" || typeof version !== "string" || typeof lang !== "string")
487
507
  return null;
488
508
  if (typeof seed !== "number" || !Number.isSafeInteger(seed) || typeof exp !== "number")
489
509
  return null;
490
- return { runId: run, seed, lang, sub, game, version, expiresAt: exp * 1000 };
510
+ const name = (v) => typeof v === "string" && /^[a-z0-9][a-z0-9-]{0,39}$/.test(v) ? v : null;
511
+ return { runId: run, seed, lang, sub, game, version, expiresAt: exp * 1000, variant: name(variant), step: name(step) };
491
512
  }
492
513
  var PRE_READY_ENTRIES = 20;
493
514
  function createCollector(opts) {
@@ -729,6 +750,7 @@ function createBridge(opts = {}) {
729
750
  let readyPromise = null;
730
751
  let launchInfo = null;
731
752
  let token = "";
753
+ let texts = {};
732
754
  let transport = "proxy";
733
755
  let finished = false;
734
756
  let tracked = 0;
@@ -930,6 +952,9 @@ function createBridge(opts = {}) {
930
952
  }
931
953
  }
932
954
  const bridge = {
955
+ t(key, vars) {
956
+ return fillText(texts[key] ?? key, vars);
957
+ },
933
958
  ready() {
934
959
  if (readyPromise)
935
960
  return readyPromise;
@@ -940,6 +965,7 @@ function createBridge(opts = {}) {
940
965
  throw new BridgeError("bad_launch", "The host sent no valid launch token.");
941
966
  token = p.token;
942
967
  transport = p.transport === "host" ? "host" : "proxy";
968
+ texts = readGameTexts(p.texts);
943
969
  launchInfo = info;
944
970
  if (vitals && !vitals.session) {
945
971
  const readyMs = perf?.sinceLoad();
@@ -1044,4 +1070,4 @@ function createBridge(opts = {}) {
1044
1070
  };
1045
1071
  return bridge;
1046
1072
  }
1047
- export { BRIDGE_VERSION, BridgeError, DEFAULT_HOST_ORIGINS, DEVICE_CLASSES, DIAG_BODY_MAX_BYTES, DIAG_LIMITS, END_REASONS, ERROR_SOURCES, FINGERPRINT_FRAMES, HOST_EVENTS, HOST_EVENT_TEXT, HOST_HANG_MS, HOST_READY_TIMEOUT_MS, JANK_FRAME_MS, LOG_LEVELS, MAX_MESSAGE_BYTES, MAX_TOKEN_LENGTH, OS_NAMES, PERF_RANGES, PRE_READY_ENTRIES, PROTOCOL, PROTOCOL_VERSION, PROXY_ORIGIN, STORAGE_KEY, STORAGE_MAX_BYTES, TRACK_EVENT, TRACK_MAX_PROPS, TRACK_MAX_STRING, TRACK_PER_LAUNCH, TRACK_PROP_KEY, boundData, cleanDiagnosticEntry, createBridge, createCollector, createHostWatchdog, createPerfSampler, decodeLaunchToken, fingerprint, fingerprintInput, finishProblem, frameKey, isCrashEntry, isFatalSource, isHostEvent, isJsonValue, isLogLevel, jsonBytes, launchContextProblem, normaliseContext, normaliseMessage, parseDiagnosticsBatch, parseGameRequest, parseHostMessage, parseStack, scrubData, scrubText, sessionIdOf, sha256HexOf, shortenGameUrls, storageKeyProblem, trackProblem, utf8Bytes };
1073
+ export { BRIDGE_VERSION, BridgeError, DEFAULT_HOST_ORIGINS, DEVICE_CLASSES, DIAG_BODY_MAX_BYTES, DIAG_LIMITS, END_REASONS, ERROR_SOURCES, FINGERPRINT_FRAMES, HOST_EVENTS, HOST_EVENT_TEXT, HOST_HANG_MS, HOST_READY_TIMEOUT_MS, JANK_FRAME_MS, LOG_LEVELS, MAX_MESSAGE_BYTES, MAX_TOKEN_LENGTH, OS_NAMES, PERF_RANGES, PRE_READY_ENTRIES, PROTOCOL, PROTOCOL_VERSION, PROXY_ORIGIN, STORAGE_KEY, STORAGE_MAX_BYTES, TRACK_EVENT, TRACK_MAX_PROPS, TRACK_MAX_STRING, TRACK_PER_LAUNCH, TRACK_PROP_KEY, boundData, cleanDiagnosticEntry, createBridge, createCollector, createHostWatchdog, createPerfSampler, decodeLaunchToken, fillText, fingerprint, fingerprintInput, finishProblem, frameKey, isCrashEntry, isFatalSource, isHostEvent, isJsonValue, isLogLevel, jsonBytes, launchContextProblem, normaliseContext, normaliseMessage, parseDiagnosticsBatch, parseGameRequest, parseHostMessage, parseStack, readGameTexts, scrubData, scrubText, sessionIdOf, sha256HexOf, shortenGameUrls, storageKeyProblem, trackProblem, utf8Bytes };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seekprotocol/game",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "GameKit: the bridge between a minigame and the Seek app.",
5
5
  "author": {
6
6
  "name": "Seek Protocol",
package/protocol.d.ts CHANGED
@@ -39,7 +39,10 @@ export interface HostEvent {
39
39
  export interface ReadyPayload {
40
40
  token: string;
41
41
  transport?: "proxy" | "host";
42
+ texts?: Record<string, string>;
42
43
  }
44
+ export declare function readGameTexts(v: unknown): Record<string, string>;
45
+ export declare function fillText(text: string, vars?: Record<string, string | number>): string;
43
46
  export interface LaunchInfo {
44
47
  runId: string;
45
48
  seed: number;
@@ -48,6 +51,8 @@ export interface LaunchInfo {
48
51
  game: string;
49
52
  version: string;
50
53
  expiresAt: number;
54
+ variant: string | null;
55
+ step: string | null;
51
56
  }
52
57
  export declare const utf8Bytes: (s: string) => number;
53
58
  export declare function jsonBytes(value: unknown): number;
package/seek-game.js CHANGED
@@ -312,6 +312,26 @@ var TRACK_MAX_STRING = 64;
312
312
  var MAX_TOKEN_LENGTH = 4096;
313
313
  var PROXY_ORIGIN = "https://api.play.seekprotocol.ai";
314
314
  var DEFAULT_HOST_ORIGINS = ["https://storeconnect.seekprotocol.ai"];
315
+ function readGameTexts(v) {
316
+ if (!isObj2(v))
317
+ return {};
318
+ const out = {};
319
+ let n = 0;
320
+ for (const [k, x] of Object.entries(v)) {
321
+ if (n >= 300)
322
+ break;
323
+ if (typeof x === "string" && x.length <= 2000 && /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,59}$/.test(k)) {
324
+ out[k] = x;
325
+ n++;
326
+ }
327
+ }
328
+ return out;
329
+ }
330
+ function fillText(text, vars) {
331
+ if (!vars)
332
+ return text;
333
+ return text.replace(/\{([A-Za-z][A-Za-z0-9_]{0,30})\}/g, (m, name) => Object.prototype.hasOwnProperty.call(vars, name) ? String(vars[name]) : m);
334
+ }
315
335
  var utf8Bytes = (s) => new TextEncoder().encode(s).byteLength;
316
336
  function jsonBytes(value) {
317
337
  try {
@@ -482,12 +502,13 @@ function decodeLaunchToken(token) {
482
502
  }
483
503
  if (!isObj2(c))
484
504
  return null;
485
- const { run, seed, lang, sub, game, version, exp } = c;
505
+ const { run, seed, lang, sub, game, version, exp, variant, step } = c;
486
506
  if (typeof run !== "string" || typeof sub !== "string" || typeof game !== "string" || typeof version !== "string" || typeof lang !== "string")
487
507
  return null;
488
508
  if (typeof seed !== "number" || !Number.isSafeInteger(seed) || typeof exp !== "number")
489
509
  return null;
490
- return { runId: run, seed, lang, sub, game, version, expiresAt: exp * 1000 };
510
+ const name = (v) => typeof v === "string" && /^[a-z0-9][a-z0-9-]{0,39}$/.test(v) ? v : null;
511
+ return { runId: run, seed, lang, sub, game, version, expiresAt: exp * 1000, variant: name(variant), step: name(step) };
491
512
  }
492
513
  var PRE_READY_ENTRIES = 20;
493
514
  function createCollector(opts) {
@@ -729,6 +750,7 @@ function createBridge(opts = {}) {
729
750
  let readyPromise = null;
730
751
  let launchInfo = null;
731
752
  let token = "";
753
+ let texts = {};
732
754
  let transport = "proxy";
733
755
  let finished = false;
734
756
  let tracked = 0;
@@ -930,6 +952,9 @@ function createBridge(opts = {}) {
930
952
  }
931
953
  }
932
954
  const bridge = {
955
+ t(key, vars) {
956
+ return fillText(texts[key] ?? key, vars);
957
+ },
933
958
  ready() {
934
959
  if (readyPromise)
935
960
  return readyPromise;
@@ -940,6 +965,7 @@ function createBridge(opts = {}) {
940
965
  throw new BridgeError("bad_launch", "The host sent no valid launch token.");
941
966
  token = p.token;
942
967
  transport = p.transport === "host" ? "host" : "proxy";
968
+ texts = readGameTexts(p.texts);
943
969
  launchInfo = info;
944
970
  if (vitals && !vitals.session) {
945
971
  const readyMs = perf?.sinceLoad();
@@ -1044,4 +1070,4 @@ function createBridge(opts = {}) {
1044
1070
  };
1045
1071
  return bridge;
1046
1072
  }
1047
- export { BRIDGE_VERSION, BridgeError, DEFAULT_HOST_ORIGINS, DEVICE_CLASSES, DIAG_BODY_MAX_BYTES, DIAG_LIMITS, END_REASONS, ERROR_SOURCES, FINGERPRINT_FRAMES, HOST_EVENTS, HOST_EVENT_TEXT, HOST_HANG_MS, HOST_READY_TIMEOUT_MS, JANK_FRAME_MS, LOG_LEVELS, MAX_MESSAGE_BYTES, MAX_TOKEN_LENGTH, OS_NAMES, PERF_RANGES, PRE_READY_ENTRIES, PROTOCOL, PROTOCOL_VERSION, PROXY_ORIGIN, STORAGE_KEY, STORAGE_MAX_BYTES, TRACK_EVENT, TRACK_MAX_PROPS, TRACK_MAX_STRING, TRACK_PER_LAUNCH, TRACK_PROP_KEY, boundData, cleanDiagnosticEntry, createBridge, createCollector, createHostWatchdog, createPerfSampler, decodeLaunchToken, fingerprint, fingerprintInput, finishProblem, frameKey, isCrashEntry, isFatalSource, isHostEvent, isJsonValue, isLogLevel, jsonBytes, launchContextProblem, normaliseContext, normaliseMessage, parseDiagnosticsBatch, parseGameRequest, parseHostMessage, parseStack, scrubData, scrubText, sessionIdOf, sha256HexOf, shortenGameUrls, storageKeyProblem, trackProblem, utf8Bytes };
1073
+ export { BRIDGE_VERSION, BridgeError, DEFAULT_HOST_ORIGINS, DEVICE_CLASSES, DIAG_BODY_MAX_BYTES, DIAG_LIMITS, END_REASONS, ERROR_SOURCES, FINGERPRINT_FRAMES, HOST_EVENTS, HOST_EVENT_TEXT, HOST_HANG_MS, HOST_READY_TIMEOUT_MS, JANK_FRAME_MS, LOG_LEVELS, MAX_MESSAGE_BYTES, MAX_TOKEN_LENGTH, OS_NAMES, PERF_RANGES, PRE_READY_ENTRIES, PROTOCOL, PROTOCOL_VERSION, PROXY_ORIGIN, STORAGE_KEY, STORAGE_MAX_BYTES, TRACK_EVENT, TRACK_MAX_PROPS, TRACK_MAX_STRING, TRACK_PER_LAUNCH, TRACK_PROP_KEY, boundData, cleanDiagnosticEntry, createBridge, createCollector, createHostWatchdog, createPerfSampler, decodeLaunchToken, fillText, fingerprint, fingerprintInput, finishProblem, frameKey, isCrashEntry, isFatalSource, isHostEvent, isJsonValue, isLogLevel, jsonBytes, launchContextProblem, normaliseContext, normaliseMessage, parseDiagnosticsBatch, parseGameRequest, parseHostMessage, parseStack, readGameTexts, scrubData, scrubText, sessionIdOf, sha256HexOf, shortenGameUrls, storageKeyProblem, trackProblem, utf8Bytes };