@my-swu/simulator-client 0.1.6 → 0.1.7

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.
Files changed (111) hide show
  1. package/README.md +29 -0
  2. package/dist/client.js +6 -1
  3. package/dist/connection/url.d.ts +2 -0
  4. package/dist/connection/url.js +4 -0
  5. package/dist/generated/index.d.ts +4 -0
  6. package/dist/generated/schemas.d.ts +1 -1
  7. package/dist/generated/schemas.js +1405 -23
  8. package/dist/generated/types/admin-match-debug-bundle.d.ts +1 -1
  9. package/dist/generated/types/admin-match-list-response.d.ts +1 -1
  10. package/dist/generated/types/admin-match-replay-response.d.ts +1 -1
  11. package/dist/generated/types/common.d.ts +445 -0
  12. package/dist/generated/types/create-limited-event-request.d.ts +1 -1
  13. package/dist/generated/types/format-event-response.d.ts +1 -1
  14. package/dist/generated/types/game-event.d.ts +1 -1
  15. package/dist/generated/types/game-state.d.ts +1 -1
  16. package/dist/generated/types/get-match-response.d.ts +1 -1
  17. package/dist/generated/types/leave-limited-event-request.d.ts +1 -0
  18. package/dist/generated/types/leave-limited-event-request.js +2 -0
  19. package/dist/generated/types/limited-event-server-message.d.ts +1 -0
  20. package/dist/generated/types/limited-event-server-message.js +2 -0
  21. package/dist/generated/types/list-active-ai-matches-response.d.ts +1 -1
  22. package/dist/generated/types/lobby-access-response.d.ts +1 -1
  23. package/dist/generated/types/lobby-chat-response.d.ts +1 -1
  24. package/dist/generated/types/lobby-response.d.ts +1 -1
  25. package/dist/generated/types/lobby-server-message.d.ts +1 -1
  26. package/dist/generated/types/match-access-recovery-response.d.ts +1 -1
  27. package/dist/generated/types/match-access-response.d.ts +1 -1
  28. package/dist/generated/types/match-history-replay-response.d.ts +1 -1
  29. package/dist/generated/types/match-recovery-response.d.ts +1 -1
  30. package/dist/generated/types/match-spectator-access-response.d.ts +1 -1
  31. package/dist/generated/types/rematch-match-response.d.ts +1 -1
  32. package/dist/generated/types/restore-history-response.d.ts +1 -1
  33. package/dist/generated/types/restore-match-save-response.d.ts +1 -1
  34. package/dist/generated/types/server-message.d.ts +1 -1
  35. package/dist/generated/types/shared-match-history-replay-response.d.ts +1 -1
  36. package/dist/generated/types/submit-ai-game-feedback-request.d.ts +1 -0
  37. package/dist/generated/types/submit-ai-game-feedback-request.js +2 -0
  38. package/dist/generated/types/submit-ai-game-feedback-response.d.ts +1 -0
  39. package/dist/generated/types/submit-ai-game-feedback-response.js +2 -0
  40. package/dist/generated/types/submit-match-deck-response.d.ts +1 -1
  41. package/dist/generated/types/tournament-room-access-response.d.ts +1 -1
  42. package/dist/generated/types/tournament-room-server-message.d.ts +1 -1
  43. package/dist/generated/types/undo-match-response.d.ts +1 -1
  44. package/dist/generated/types/update-lobby-settings-request.d.ts +1 -1
  45. package/dist/generated/version.d.ts +1 -1
  46. package/dist/generated/version.js +1 -1
  47. package/dist/http/request.d.ts +2 -0
  48. package/dist/http/request.js +9 -1
  49. package/dist/i18n/card-print-catalog.d.ts +9 -0
  50. package/dist/i18n/card-print-catalog.js +260 -0
  51. package/dist/i18n/catalog.d.ts +8 -0
  52. package/dist/i18n/catalog.js +195 -0
  53. package/dist/i18n/diagnostics.d.ts +6 -0
  54. package/dist/i18n/diagnostics.js +19 -0
  55. package/dist/i18n/fallback.d.ts +3 -0
  56. package/dist/i18n/fallback.js +40 -0
  57. package/dist/i18n/format.d.ts +8 -0
  58. package/dist/i18n/format.js +29 -0
  59. package/dist/i18n/generated/message-types.d.ts +91 -0
  60. package/dist/i18n/generated/message-types.js +3 -0
  61. package/dist/i18n/icu.d.ts +12 -0
  62. package/dist/i18n/icu.js +75 -0
  63. package/dist/i18n/index.d.ts +9 -0
  64. package/dist/i18n/index.js +5 -0
  65. package/dist/i18n/locale.d.ts +9 -0
  66. package/dist/i18n/locale.js +19 -0
  67. package/dist/i18n/types.d.ts +151 -0
  68. package/dist/i18n/types.js +1 -0
  69. package/dist/index.d.ts +4 -3
  70. package/dist/internal/authenticated-socket-session.d.ts +2 -0
  71. package/dist/internal/authenticated-socket-session.js +3 -1
  72. package/dist/local/client.js +7 -2
  73. package/dist/local/server/http-router.js +18 -0
  74. package/dist/resources/admin.d.ts +8 -0
  75. package/dist/resources/admin.js +2 -0
  76. package/dist/resources/limited-event-session.d.ts +10 -0
  77. package/dist/resources/limited-event-session.js +35 -0
  78. package/dist/resources/limited-event-types.d.ts +34 -1
  79. package/dist/resources/limited-events.d.ts +8 -1
  80. package/dist/resources/limited-events.js +18 -1
  81. package/dist/resources/lobbies.d.ts +1 -1
  82. package/dist/resources/lobbies.js +1 -1
  83. package/dist/resources/lobby-types.d.ts +15 -2
  84. package/dist/resources/match-history.d.ts +8 -1
  85. package/dist/resources/match-history.js +10 -0
  86. package/dist/resources/tournament-rooms.d.ts +1 -1
  87. package/dist/resources/tournament-rooms.js +1 -1
  88. package/dist/validators.d.ts +5 -1
  89. package/i18n/cards/de.json +121 -0
  90. package/i18n/cards/en.json +121 -0
  91. package/i18n/cards/es.json +121 -0
  92. package/i18n/cards/fr.json +121 -0
  93. package/i18n/cards/it.json +121 -0
  94. package/i18n/cards/manifest.json +2715 -0
  95. package/i18n/de.json +11 -0
  96. package/i18n/en.json +11 -0
  97. package/i18n/es.json +11 -0
  98. package/i18n/fr.json +11 -0
  99. package/i18n/it.json +11 -0
  100. package/i18n/messages.schema.json +28 -0
  101. package/package.json +12 -2
  102. package/schema/common.schema.json +1092 -1
  103. package/schema/create-limited-event-request.schema.json +11 -0
  104. package/schema/game-event.schema.json +76 -0
  105. package/schema/leave-limited-event-request.schema.json +15 -0
  106. package/schema/limited-event-server-message.schema.json +63 -0
  107. package/schema/manifest.json +5 -1
  108. package/schema/route-manifest.json +54 -0
  109. package/schema/submit-ai-game-feedback-request.schema.json +36 -0
  110. package/schema/submit-ai-game-feedback-response.schema.json +19 -0
  111. package/schema/update-lobby-settings-request.schema.json +67 -0
@@ -0,0 +1,91 @@
1
+ /** Generated from i18n/messages.schema.json. Do not edit. */
2
+ export declare const simulatorMessageIds: readonly ["abilityUnavailable", "action.exhaust", "amount.resources", "choice.none", "chooseTarget", "chooseTargets", "controls.cancel", "controls.confirm", "controls.decline", "controls.done", "duration.phase", "duration.rounds", "keyword.name", "keyword.overwhelm", "keyword.raid", "keyword.sentinel", "legacyText", "targetPurpose.attachUpgrade", "targetPurpose.chooseTarget", "targetPurpose.dealDamage", "targetPurpose.giveShield", "targetPurpose.heal", "timing.onceEachRound"];
3
+ export declare const simulatorMessageParameterTypes: {
4
+ readonly abilityUnavailable: {};
5
+ readonly "action.exhaust": {};
6
+ readonly "amount.resources": {
7
+ readonly count: "integer";
8
+ };
9
+ readonly "choice.none": {};
10
+ readonly chooseTarget: {};
11
+ readonly chooseTargets: {
12
+ readonly min: "integer";
13
+ readonly max: "integer";
14
+ };
15
+ readonly "controls.cancel": {};
16
+ readonly "controls.confirm": {};
17
+ readonly "controls.decline": {};
18
+ readonly "controls.done": {};
19
+ readonly "duration.phase": {};
20
+ readonly "duration.rounds": {
21
+ readonly count: "integer";
22
+ };
23
+ readonly "keyword.name": {
24
+ readonly keyword: "string";
25
+ };
26
+ readonly "keyword.overwhelm": {};
27
+ readonly "keyword.raid": {};
28
+ readonly "keyword.sentinel": {};
29
+ readonly legacyText: {
30
+ readonly text: "string";
31
+ };
32
+ readonly "targetPurpose.attachUpgrade": {};
33
+ readonly "targetPurpose.chooseTarget": {};
34
+ readonly "targetPurpose.dealDamage": {
35
+ readonly amount: "integer";
36
+ };
37
+ readonly "targetPurpose.giveShield": {
38
+ readonly amount: "integer";
39
+ };
40
+ readonly "targetPurpose.heal": {
41
+ readonly amount: "integer";
42
+ };
43
+ readonly "timing.onceEachRound": {};
44
+ };
45
+ export type SimulatorMessageId = (typeof simulatorMessageIds)[number];
46
+ export interface SimulatorMessageParams {
47
+ "abilityUnavailable": {};
48
+ "action.exhaust": {};
49
+ "amount.resources": {
50
+ count: number;
51
+ };
52
+ "choice.none": {};
53
+ "chooseTarget": {};
54
+ "chooseTargets": {
55
+ min: number;
56
+ max: number;
57
+ };
58
+ "controls.cancel": {};
59
+ "controls.confirm": {};
60
+ "controls.decline": {};
61
+ "controls.done": {};
62
+ "duration.phase": {};
63
+ "duration.rounds": {
64
+ count: number;
65
+ };
66
+ "keyword.name": {
67
+ keyword: string;
68
+ };
69
+ "keyword.overwhelm": {};
70
+ "keyword.raid": {};
71
+ "keyword.sentinel": {};
72
+ "legacyText": {
73
+ text: string;
74
+ };
75
+ "targetPurpose.attachUpgrade": {};
76
+ "targetPurpose.chooseTarget": {};
77
+ "targetPurpose.dealDamage": {
78
+ amount: number;
79
+ };
80
+ "targetPurpose.giveShield": {
81
+ amount: number;
82
+ };
83
+ "targetPurpose.heal": {
84
+ amount: number;
85
+ };
86
+ "timing.onceEachRound": {};
87
+ }
88
+ /** A message id whose parameter map has no required keys. */
89
+ export type ParameterlessSimulatorMessageId = {
90
+ [TId in SimulatorMessageId]: keyof SimulatorMessageParams[TId] extends never ? TId : never;
91
+ }[SimulatorMessageId];
@@ -0,0 +1,3 @@
1
+ /** Generated from i18n/messages.schema.json. Do not edit. */
2
+ export const simulatorMessageIds = ["abilityUnavailable", "action.exhaust", "amount.resources", "choice.none", "chooseTarget", "chooseTargets", "controls.cancel", "controls.confirm", "controls.decline", "controls.done", "duration.phase", "duration.rounds", "keyword.name", "keyword.overwhelm", "keyword.raid", "keyword.sentinel", "legacyText", "targetPurpose.attachUpgrade", "targetPurpose.chooseTarget", "targetPurpose.dealDamage", "targetPurpose.giveShield", "targetPurpose.heal", "timing.onceEachRound"];
3
+ export const simulatorMessageParameterTypes = { "abilityUnavailable": {}, "action.exhaust": {}, "amount.resources": { "count": "integer" }, "choice.none": {}, "chooseTarget": {}, "chooseTargets": { "min": "integer", "max": "integer" }, "controls.cancel": {}, "controls.confirm": {}, "controls.decline": {}, "controls.done": {}, "duration.phase": {}, "duration.rounds": { "count": "integer" }, "keyword.name": { "keyword": "string" }, "keyword.overwhelm": {}, "keyword.raid": {}, "keyword.sentinel": {}, "legacyText": { "text": "string" }, "targetPurpose.attachUpgrade": {}, "targetPurpose.chooseTarget": {}, "targetPurpose.dealDamage": { "amount": "integer" }, "targetPurpose.giveShield": { "amount": "integer" }, "targetPurpose.heal": { "amount": "integer" }, "timing.onceEachRound": {} };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Parses a plain ICU template and verifies its argument names and specialized types.
3
+ * Bare interpolation accepts every declared scalar type; only ICU format operators constrain it.
4
+ *
5
+ * @param {string} locale Locale used only to parse and compile plural syntax.
6
+ * @param {string} id Stable message id used in errors.
7
+ * @param {string} template ICU message source.
8
+ * @param {Record<string, 'string' | 'number' | 'integer' | 'boolean'>} declaration Exact typed parameter map.
9
+ * @returns {IntlMessageFormat} Prepared formatter for the checked template.
10
+ */
11
+ export function validateIcuMessage(locale: string, id: string, template: string, declaration: Record<string, "string" | "number" | "integer" | "boolean">): IntlMessageFormat;
12
+ import IntlMessageFormat from 'intl-messageformat';
@@ -0,0 +1,75 @@
1
+ import IntlMessageFormat from 'intl-messageformat';
2
+ /**
3
+ * Parses a plain ICU template and verifies its argument names and specialized types.
4
+ * Bare interpolation accepts every declared scalar type; only ICU format operators constrain it.
5
+ *
6
+ * @param {string} locale Locale used only to parse and compile plural syntax.
7
+ * @param {string} id Stable message id used in errors.
8
+ * @param {string} template ICU message source.
9
+ * @param {Record<string, 'string' | 'number' | 'integer' | 'boolean'>} declaration Exact typed parameter map.
10
+ * @returns {IntlMessageFormat} Prepared formatter for the checked template.
11
+ */
12
+ export function validateIcuMessage(locale, id, template, declaration) {
13
+ if (typeof template !== 'string')
14
+ throw new Error(`${locale}:${id} is not a string`);
15
+ if (/<\/?[a-z][^>]*>/i.test(template))
16
+ throw new Error(`${locale}:${id} uses unsafe rich tags`);
17
+ let formatter;
18
+ try {
19
+ formatter = new IntlMessageFormat(template, locale);
20
+ }
21
+ catch (error) {
22
+ throw new Error(`${locale}:${id} has malformed ICU: ${error.message}`);
23
+ }
24
+ const names = new Set();
25
+ const constraints = new Map();
26
+ collectArguments(formatter.getAst(), names, constraints);
27
+ const expected = Object.keys(declaration).sort();
28
+ if ([...names].sort().join('\0') !== expected.join('\0')) {
29
+ throw new Error(`${locale}:${id} has arguments that differ from schema`);
30
+ }
31
+ for (const [name, type] of constraints) {
32
+ // ICU's `number` formatter accepts both general SDK numbers and the
33
+ // integer-only values used by persisted engine messages.
34
+ if (declaration[name] !== type && !(type === 'number' && declaration[name] === 'integer')) {
35
+ throw new Error(`${locale}:${id} argument ${name} must be ${type}`);
36
+ }
37
+ }
38
+ return formatter;
39
+ }
40
+ /**
41
+ * Collects all argument names and only constraints imposed by ICU operators.
42
+ *
43
+ * @param {unknown[]} elements FormatJS parser nodes.
44
+ * @param {Set<string>} names Observed arguments.
45
+ * @param {Map<string, 'string' | 'number'>} constraints Specialized value requirements.
46
+ */
47
+ function collectArguments(elements, names, constraints) {
48
+ for (const element of elements) {
49
+ if (element.type === 8)
50
+ throw new Error('ICU rich tags are not allowed in SDK catalogs');
51
+ if (element.type >= 1 && element.type <= 6) {
52
+ names.add(element.value);
53
+ const type = element.type === 5 ? 'string' : element.type === 1 ? null : 'number';
54
+ if (type != null)
55
+ addConstraint(element.value, type, constraints);
56
+ }
57
+ if (element.options != null) {
58
+ for (const option of Object.values(element.options))
59
+ collectArguments(option.value, names, constraints);
60
+ }
61
+ }
62
+ }
63
+ /**
64
+ * Adds a specialized ICU value requirement, rejecting incompatible reuse.
65
+ *
66
+ * @param {string} name Argument name.
67
+ * @param {'string' | 'number'} type Required value type.
68
+ * @param {Map<string, 'string' | 'number'>} constraints Collected requirements.
69
+ */
70
+ function addConstraint(name, type, constraints) {
71
+ const prior = constraints.get(name);
72
+ if (prior != null && prior !== type)
73
+ throw new Error(`ICU argument ${name} has conflicting types`);
74
+ constraints.set(name, type);
75
+ }
@@ -0,0 +1,9 @@
1
+ export { prepareCardTextCatalog, prepareMessageCatalog } from './catalog.js';
2
+ export { createCardTextResolver } from './diagnostics.js';
3
+ export { formatLegacyLiteral, formatMessage } from './format.js';
4
+ export { resolveCardText } from './fallback.js';
5
+ export { resolveLocale } from './locale.js';
6
+ export type { CardTextCatalogInput, CardTextCatalogManifestInput, CardTextCatalogSlotInput, CardTextCatalogSlotStatus, CardPrintEffectiveBaseline, CardPrintRequestedField, CardPrintResolvedField, CardPrintSourceField, CardPrintSourceFieldPresence, CardPrintTextCatalogField, CardPrintTextCatalogInput, CardPrintTextCatalogStatus, CardPrintTextField, CardTextDiagnostic, CardTextDiagnosticSink, CardTextCatalogShardInfo, CardTextFallbackReason, CardTextResolver, LegacyLiteralText, PreparedCardTextCatalog, PreparedMessageCatalog, ResolvedCardText, SimulatorMessageCatalogInput, SimulatorMessageId, SimulatorMessageParams, } from './types.js';
7
+ export type { CardTextRef } from '../generated/types/common.js';
8
+ export type { SimulatorLocale } from './locale.js';
9
+ export type { ParameterlessSimulatorMessageId } from './generated/message-types.js';
@@ -0,0 +1,5 @@
1
+ export { prepareCardTextCatalog, prepareMessageCatalog } from './catalog.js';
2
+ export { createCardTextResolver } from './diagnostics.js';
3
+ export { formatLegacyLiteral, formatMessage } from './format.js';
4
+ export { resolveCardText } from './fallback.js';
5
+ export { resolveLocale } from './locale.js';
@@ -0,0 +1,9 @@
1
+ /** SDK locales with reviewed simulator message and card catalogs. */
2
+ export type SimulatorLocale = 'de' | 'en' | 'es' | 'fr' | 'it';
3
+ /**
4
+ * Converts a browser, HTTP, or persisted locale input to one supported base locale.
5
+ *
6
+ * Regional variants use their base language. Malformed and unsupported inputs use
7
+ * English so callers never depend on ambient runtime locale state.
8
+ */
9
+ export declare function resolveLocale(value: string | null | undefined): SimulatorLocale;
@@ -0,0 +1,19 @@
1
+ const supportedLocales = new Set(['de', 'en', 'es', 'fr', 'it']);
2
+ /**
3
+ * Converts a browser, HTTP, or persisted locale input to one supported base locale.
4
+ *
5
+ * Regional variants use their base language. Malformed and unsupported inputs use
6
+ * English so callers never depend on ambient runtime locale state.
7
+ */
8
+ export function resolveLocale(value) {
9
+ if (value == null)
10
+ return 'en';
11
+ try {
12
+ const canonical = Intl.getCanonicalLocales(value.trim())[0];
13
+ const base = canonical == null ? undefined : new Intl.Locale(canonical).language;
14
+ return base != null && supportedLocales.has(base) ? base : 'en';
15
+ }
16
+ catch {
17
+ return 'en';
18
+ }
19
+ }
@@ -0,0 +1,151 @@
1
+ import type IntlMessageFormat from 'intl-messageformat';
2
+ import type { CardTextRef } from '../generated/types/common.js';
3
+ import type { SimulatorLocale } from './locale.js';
4
+ import type { SimulatorMessageId, SimulatorMessageParams } from './generated/message-types.js';
5
+ /** Stable semantic reference generated from the simulator wire contract. */
6
+ export type { CardTextRef } from '../generated/types/common.js';
7
+ /** One checked-in ICU message pack before validation and formatter compilation. */
8
+ export interface SimulatorMessageCatalogInput {
9
+ formatVersion: number;
10
+ locale: string;
11
+ messages: Record<string, string>;
12
+ }
13
+ /** Immutable prepared UI catalog for one locale. */
14
+ export interface PreparedMessageCatalog {
15
+ readonly locale: SimulatorLocale;
16
+ readonly messages: Readonly<Record<SimulatorMessageId, string>>;
17
+ readonly formatters: ReadonlyMap<SimulatorMessageId, IntlMessageFormat>;
18
+ }
19
+ /** Stable exact reference to reviewed literal printed card text. */
20
+ /** Tagged pre-reference text retained only for old saves and history payloads. */
21
+ export interface LegacyLiteralText {
22
+ readonly kind: 'legacyLiteral';
23
+ readonly text: string;
24
+ }
25
+ /** Literal card catalog source loaded through an explicit asset boundary. */
26
+ export interface CardTextCatalogInput {
27
+ formatVersion: number;
28
+ locale: string;
29
+ cards: Record<string, Record<string, CardTextCatalogSlotInput>>;
30
+ /** Present and required in format 2 full-print catalogs. */
31
+ prints?: Record<string, CardPrintTextCatalogInput>;
32
+ }
33
+ /** One compiler-emitted literal slot. */
34
+ export interface CardTextCatalogSlotInput {
35
+ plainText: string;
36
+ sourceLocale: string;
37
+ status: CardTextCatalogSlotStatus;
38
+ }
39
+ /** Compiler-declared source state retained for exact fallback diagnostics. */
40
+ export type CardTextCatalogSlotStatus = {
41
+ kind: 'source';
42
+ } | {
43
+ kind: 'fallback';
44
+ reason: CardTextFallbackReason;
45
+ } | {
46
+ kind: 'notApplicable';
47
+ };
48
+ /** Plain printed fields exposed for one requested physical production print. */
49
+ export type CardPrintTextField = 'title' | 'subtitle' | 'text' | 'deployBox' | 'epicAction' | 'rules';
50
+ /** Lossless resolved source field state from the production localization payload. */
51
+ export type CardPrintSourceField = {
52
+ state: 'missing';
53
+ } | {
54
+ state: 'null';
55
+ } | {
56
+ state: 'value';
57
+ value: string;
58
+ };
59
+ /** Requested field state retained without shipping stale source prose twice. */
60
+ export type CardPrintRequestedField = {
61
+ kind: 'missingLocale';
62
+ requestedPrintId: number;
63
+ } | {
64
+ kind: 'source';
65
+ sourcePrintId: number;
66
+ sourceLocale: string;
67
+ sourceRevision: string;
68
+ presence: CardPrintSourceFieldPresence;
69
+ };
70
+ /** Exact requested source-field state, including explicit empty `value` separately. */
71
+ export type CardPrintSourceFieldPresence = 'missing' | 'null' | 'value';
72
+ /** Identity of an effective English replacement for stale raw source. */
73
+ export interface CardPrintEffectiveBaseline {
74
+ canonicalCardId: number;
75
+ semanticFingerprint: string;
76
+ }
77
+ /** Exact source selected for a full printed field. */
78
+ export interface CardPrintResolvedField {
79
+ sourcePrintId: number;
80
+ sourceLocale: string;
81
+ sourceRevision: string;
82
+ field: CardPrintSourceField;
83
+ effectiveBaseline?: CardPrintEffectiveBaseline;
84
+ }
85
+ /** Full-field fallback policy keeps optional absence and explicit empty distinct. */
86
+ export type CardPrintTextCatalogStatus = {
87
+ kind: 'source';
88
+ } | {
89
+ kind: 'fallback';
90
+ reason: 'missingLocale' | 'missingRequiredTitle' | 'missingSourceField' | 'staleEffectiveText';
91
+ };
92
+ /** Requested and resolved provenance for one full printed field. */
93
+ export interface CardPrintTextCatalogField {
94
+ requested: CardPrintRequestedField;
95
+ resolved: CardPrintResolvedField;
96
+ status: CardPrintTextCatalogStatus;
97
+ }
98
+ /** Full printed fields for one physical production print. */
99
+ export interface CardPrintTextCatalogInput {
100
+ canonicalCardId: number;
101
+ requestedPrintId: number;
102
+ fields: Record<CardPrintTextField, CardPrintTextCatalogField>;
103
+ }
104
+ /** Reviewed reason why one localized printed slot resolves to English. */
105
+ export type CardTextFallbackReason = 'missingLocale' | 'staleEffectiveText' | 'unreviewedSegmentation';
106
+ /** Identity asset shared by separately loaded locale shards. */
107
+ export interface CardTextCatalogManifestInput {
108
+ formatVersion: number;
109
+ contentHash: string;
110
+ sourceGeneration: string;
111
+ englishBaseline: string;
112
+ localeShards: Record<SimulatorLocale, CardTextCatalogShardInfo>;
113
+ canonicalAliases: Record<string, number>;
114
+ contractFingerprints: Record<string, string>;
115
+ }
116
+ /** Canonical byte identity of one independently loaded locale shard. */
117
+ export interface CardTextCatalogShardInfo {
118
+ contentHash: string;
119
+ byteSize: number;
120
+ }
121
+ /** Prepared literal card catalog, after structural validation. */
122
+ export interface PreparedCardTextCatalog {
123
+ readonly locale: SimulatorLocale;
124
+ readonly manifest: CardTextCatalogManifestInput;
125
+ readonly cards: Readonly<Record<string, Readonly<Record<string, CardTextCatalogSlotInput>>>>;
126
+ readonly prints: Readonly<Record<string, Readonly<CardPrintTextCatalogInput>>>;
127
+ }
128
+ /** Readable result for one exact reference lookup. */
129
+ export interface ResolvedCardText {
130
+ readonly plainText: string;
131
+ readonly resolvedLocale: SimulatorLocale;
132
+ readonly status: 'source' | 'fallback' | 'notApplicable' | 'unavailable';
133
+ readonly fallbackReason?: CardTextFallbackReason;
134
+ readonly diagnostic?: 'fingerprintMismatch' | 'notApplicable' | 'unknownReference';
135
+ }
136
+ /** Machine-readable unavailable-reference record for consumer-owned diagnostics. */
137
+ export interface CardTextDiagnostic {
138
+ readonly kind: 'fingerprintMismatch' | 'unknownReference';
139
+ readonly cardId: number;
140
+ readonly slot: string;
141
+ readonly contractFingerprint: string;
142
+ }
143
+ /** Receives a deduplicated unavailable-reference record from one resolver instance. */
144
+ export type CardTextDiagnosticSink = (diagnostic: CardTextDiagnostic) => void;
145
+ /** Instance-scoped literal-card resolver with a consumer-selected diagnostic boundary. */
146
+ export interface CardTextResolver {
147
+ /** Resolves one exact literal-card reference without exposing unavailable raw keys. */
148
+ resolve(catalog: PreparedCardTextCatalog, reference: CardTextRef): ResolvedCardText;
149
+ }
150
+ /** Exact generated parameter map for one UI message id. */
151
+ export type { SimulatorMessageId, SimulatorMessageParams };
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -6,13 +6,14 @@ export type { CloseTournamentRoomRequest, CreateTournamentRoomRequest, CreateTou
6
6
  export { canDeployAnyLeader, flattenTargetGroups, getDeployableLeaderIndices, getLeaderDeployCommands, isSingleTargetPlan, leaderDeployCommand, targetGroupsOf, } from './actionable.js';
7
7
  export type { TargetingActionView } from './actionable.js';
8
8
  export type { SimulatorClient, SimulatorClientOptions } from './client.js';
9
- export type { AbandonMatchRequest, AbandonMatchResponse, ApiErrorResponse, AiStatusPayload, CardsResponse, CardCatalogRefreshResponse, ClientMessage, CreateLimitedEventRequest, CreateMetaWindowRequest, CreateMatchRequest, ErrorPayload, FormatEventResponse, FormatEventRequest, GameCommand, GameEvent, GameState, GetMatchResponse, HealthResponse, JoinLimitedEventRequest, JoinMatchRequest, LeaveMatchRequest, LeaveMatchResponse, LimitedEventAccessResponse, LimitedEventCommandRequest, LimitedEventPreflightResponse, LimitedEventResponse, ListActiveAiMatchesRequest, ListActiveAiMatchesResponse, MatchAccessResponse, MatchChatLookupResponse, MatchConnectionRole, MatchHistoryDeckStatsRequest, MatchHistoryDeckStatsResponse, MatchHistoryListRequest, MatchHistoryListResponse, MatchHistoryReplayRequest, MatchHistoryReplayResponse, MatchHistoryRestoreRequest, MatchRecoveryRequest, MatchRecoveryResponse, RestoreHistoryResponse, MatchSavePayload, MatchSpectatorAccessResponse, MetaWindow, MetaWindowsResponse, ResolveCardIdsRequest, ResolveCardIdsResponse, RankedAdminStatus, RankedLeaderboardEntry, RankedLeaderboardQuery, RankedLeaderboardResponse, RankedMatchmakingView, RankedProfileRequest, RankedProfileResponse, RankedQueue, RematchMatchRequest, RematchMatchResponse, RestoreMatchSaveRequest, RestoreMatchSaveResponse, SaveMatchRequest, ServerMessage, SetAiPausedRequest, SetRankedAdmissionRequest, SetSimulatorMaintenanceRequest, SimulatorAdminStatusResponse, SimulatorAvailabilityResponse, SpectateMatchRequest, SubmitMatchDeckRequest, SubmitMatchDeckResponse, UndoMatchRequest, UndoMatchResponse, UndoRespondMatchRequest, WelcomePayload, } from './generated/index.js';
9
+ export type { AbandonMatchRequest, AbandonMatchResponse, ApiErrorResponse, AiStatusPayload, CardsResponse, CardCatalogRefreshResponse, ClientMessage, CreateLimitedEventRequest, CreateMetaWindowRequest, CreateMatchRequest, ErrorPayload, FormatEventResponse, FormatEventRequest, GameCommand, GameEvent, GameState, GetMatchResponse, HealthResponse, JoinLimitedEventRequest, JoinMatchRequest, LeaveMatchRequest, LeaveMatchResponse, LimitedEventAccessResponse, LimitedEventServerMessage, LimitedEventCommandRequest, LimitedEventPreflightResponse, LimitedEventResponse, ListActiveAiMatchesRequest, ListActiveAiMatchesResponse, MatchAccessResponse, MatchChatLookupResponse, MatchConnectionRole, MatchHistoryDeckStatsRequest, MatchHistoryDeckStatsResponse, MatchHistoryListRequest, MatchHistoryListResponse, MatchHistoryReplayRequest, MatchHistoryReplayResponse, MatchHistoryRestoreRequest, MatchRecoveryRequest, MatchRecoveryResponse, RestoreHistoryResponse, MatchSavePayload, MatchSpectatorAccessResponse, MetaWindow, MetaWindowsResponse, ResolveCardIdsRequest, ResolveCardIdsResponse, RankedAdminStatus, RankedLeaderboardEntry, RankedLeaderboardQuery, RankedLeaderboardResponse, RankedMatchmakingView, RankedProfileRequest, RankedProfileResponse, RankedQueue, RematchMatchRequest, RematchMatchResponse, RestoreMatchSaveRequest, RestoreMatchSaveResponse, SaveMatchRequest, ServerMessage, SetAiPausedRequest, SetRankedAdmissionRequest, SetSimulatorMaintenanceRequest, SimulatorAdminStatusResponse, SimulatorAvailabilityResponse, SpectateMatchRequest, SubmitAiGameFeedbackRequest, SubmitAiGameFeedbackResponse, SubmitMatchDeckRequest, SubmitMatchDeckResponse, UndoMatchRequest, UndoMatchResponse, UndoRespondMatchRequest, WelcomePayload, } from './generated/index.js';
10
10
  export type { PlaygroundCommand, PlaygroundDamageTarget, PlaygroundDefeatTarget, } from './generated/types/game-command.js';
11
11
  export type { UndoCancelReason } from './generated/types/game-event.js';
12
12
  export type { LimitedSideboardPoolView } from './generated/types/game-state.js';
13
13
  export type { HistoryIdentityInput, HistorySection, HistoryStatus, } from './generated/types/match-history-list-request.js';
14
14
  export type { MatchHistoryEntry, MatchHistoryPlayerSummary, } from './generated/types/match-history-entry.js';
15
15
  export type { MatchHistoryReplayArchive } from './generated/types/match-history-replay-response.js';
16
+ export type { AiGameFeedback, AiGameFeedbackRating, } from './generated/types/submit-ai-game-feedback-response.js';
16
17
  export type { DeckArchetype, DeckArchetypeMatchup, DeckEndReasonCount, DeckFormatStats, DeckOpponentStats, DeckStats, DeckStatsArchetype, DeckStatsOpponentKind, WinLossDrawRecord, } from './generated/types/match-history-deck-stats-response.js';
17
18
  export { schemaVersion } from './generated/version.js';
18
19
  export type { FormatEventCommand } from './generated/types/format-event-request.js';
@@ -27,8 +28,8 @@ export type { CardInteractionCueRequest, } from './generated/types/client-messag
27
28
  export type { CardInteractionCue, CardInteractionCueKind, CardInteractionCueState, CardInteractionDragOffset, MatchChatMessage, MatchHistoryFeedEntry, MatchHistorySnapshot, MatchQuickPhrase, } from './generated/types/server-message.js';
28
29
  export type { CardIdResolution, CardMetadata, CardReferenceInput, CardReferenceType, DeckInput, DeckJson, DeckJsonCard, } from './resources/card-types.js';
29
30
  export type { CardsResource } from './resources/card-resource.js';
30
- export type { LimitedEventAccess, LimitedEventAccessInput, LimitedEventCommand, LimitedEventPreflight, LimitedEventReference, LimitedEventSnapshot, LimitedEventsResource, } from './resources/limited-events.js';
31
- export type { MatchHistoryResource, MatchRecoveryInput, MatchHistoryReplayInput, MatchHistoryRestoreInput, } from './resources/match-history.js';
31
+ export type { LimitedEventAccess, LimitedEventAccessInput, LimitedEventCommand, LimitedEventPreflight, LimitedEventReference, LimitedEventSession, LimitedEventSessionEvents, LimitedEventSessionState, LimitedEventSnapshot, LimitedEventsResource, } from './resources/limited-events.js';
32
+ export type { MatchHistoryResource, MatchRecoveryInput, MatchHistoryReplayInput, MatchHistoryRestoreInput, SubmitAiGameFeedbackInput, } from './resources/match-history.js';
32
33
  export type { MatchAccess, MatchAccessInput, MatchConnectionAccessInput, MatchReference, MatchSpectatorAccess, MatchSpectatorAccessInput, MatchesResource, } from './resources/matches.js';
33
34
  export type { SessionResource } from './resources/session.js';
34
35
  export type { SystemResource } from './resources/system.js';
@@ -41,6 +41,8 @@ export declare abstract class AuthenticatedSocketSessionController<TMessage, TSn
41
41
  /** Boundary validator; defaults to the pass-through, which checks nothing. */
42
42
  validate?: SchemaValidator | undefined;
43
43
  webSocketFactory: WebSocketFactory;
44
+ /** Encodes credential kind and token as WebSocket subprotocols. */
45
+ tokenProtocols?: ((token: string) => string[]) | undefined;
44
46
  });
45
47
  get connectionState(): ConnectionState;
46
48
  get errorMessages(): readonly string[];
@@ -22,6 +22,7 @@ export class AuthenticatedSocketSessionController {
22
22
  #socketUrl;
23
23
  #validator;
24
24
  #webSocketFactory;
25
+ #tokenProtocols;
25
26
  #connectionState = 'idle';
26
27
  #errors = [];
27
28
  /** Previous live socket held until its replacement has opened. */
@@ -34,6 +35,7 @@ export class AuthenticatedSocketSessionController {
34
35
  this.#socketUrl = options.socketUrl;
35
36
  this.#validator = options.validate ?? passthroughValidator;
36
37
  this.#webSocketFactory = options.webSocketFactory;
38
+ this.#tokenProtocols = options.tokenProtocols ?? playerTokenProtocols;
37
39
  this.#playerToken = options.playerToken;
38
40
  }
39
41
  #playerToken;
@@ -57,7 +59,7 @@ export class AuthenticatedSocketSessionController {
57
59
  const previousSocket = this.#socket;
58
60
  this.#replacedSocket = previousSocket;
59
61
  this.#setConnectionState('connecting');
60
- const socket = this.#webSocketFactory(this.#socketUrl, playerTokenProtocols(this.#playerToken));
62
+ const socket = this.#webSocketFactory(this.#socketUrl, this.#tokenProtocols(this.#playerToken));
61
63
  this.#socket = socket;
62
64
  let opened = false;
63
65
  socket.addEventListener('open', () => {
@@ -4,6 +4,10 @@
4
4
  * `createSimulatorClient` lights up the entire existing SDK (resources,
5
5
  * `MatchSession`, reconnect) against the in-browser engine with zero changes.
6
6
  */
7
+ /** Whether an HTTP response status permits a message body. */
8
+ function responseMayHaveBody(status) {
9
+ return status !== 204 && status !== 205 && status !== 304;
10
+ }
7
11
  /**
8
12
  * Creates the transport over a running engine worker (or a test double).
9
13
  */
@@ -98,8 +102,9 @@ export function createWorkerTransport(port) {
98
102
  path: `${parsed.pathname}${parsed.search}`,
99
103
  body,
100
104
  });
101
- return new Response(result.body, {
102
- status: result.status ?? 200,
105
+ const status = result.status ?? 200;
106
+ return new Response(responseMayHaveBody(status) ? result.body : null, {
107
+ status,
103
108
  headers: { 'content-type': 'application/json' },
104
109
  });
105
110
  };
@@ -25,6 +25,14 @@ function retainOnlyMatch(registry, kept) {
25
25
  }
26
26
  }
27
27
  }
28
+ /** Removes reverse limited-event ownership entries for one discarded lobby. */
29
+ function deleteLobbyEventMappings(registry, lobbyId) {
30
+ for (const [eventId, owningLobbyId] of registry.eventLobbies) {
31
+ if (owningLobbyId === lobbyId) {
32
+ registry.eventLobbies.delete(eventId);
33
+ }
34
+ }
35
+ }
28
36
  /**
29
37
  * Routes one HTTP request against the engine.
30
38
  */
@@ -62,6 +70,15 @@ export function routeHttpRequest(engine, registry, method, path, body, nowMs) {
62
70
  const seatToken = url.searchParams.get('seatToken');
63
71
  return { status: 200, body: lobby.eventSnapshot(seatToken, nowMs) };
64
72
  }
73
+ if (segments[3] === 'leave' && segments.length === 4 && method === 'POST') {
74
+ if (body == null) {
75
+ return jsonError(400, 'invalidRequest', 'missing leave request body');
76
+ }
77
+ // Local Limited events are always started from a lobby, so the lobby
78
+ // owns departure and replacement. Mirror the online direct-event guard
79
+ // without deleting the event mapping behind the active offline lobby.
80
+ return jsonError(409, 'limited_event_presence_owned_by_room', 'limited event presence is owned by its room');
81
+ }
65
82
  if (segments[3] === 'commands' && method === 'POST') {
66
83
  if (body == null) {
67
84
  return jsonError(400, 'invalidRequest', 'missing command body');
@@ -186,6 +203,7 @@ function routeLobbyRequest(engine, registry, segments, method, body, nowMs) {
186
203
  }
187
204
  case 'POST leave': {
188
205
  const response = lobby.accessJson();
206
+ deleteLobbyEventMappings(registry, lobbyId);
189
207
  registry.lobbies.delete(lobbyId);
190
208
  return { status: 200, body: response };
191
209
  }
@@ -36,6 +36,14 @@ export interface AdminResource {
36
36
  getGameDebugBundle(auth: SimulatorAdminAuth, historyId: string, options?: SimulatorRequestOptions): Promise<AdminMatchDebugBundle>;
37
37
  /** Returns a completed replay archive for one match. */
38
38
  getGameReplay(auth: SimulatorAdminAuth, historyId: string, options?: SimulatorRequestOptions): Promise<AdminMatchReplayResponse>;
39
+ /**
40
+ * Returns all diagnostics for one match by runtime match id, including a
41
+ * match that is still in progress (the live runtime is exported first, the
42
+ * durable row serves once the runtime is gone).
43
+ */
44
+ getMatchDebugBundle(auth: SimulatorAdminAuth, matchId: string, options?: SimulatorRequestOptions): Promise<AdminMatchDebugBundle>;
45
+ /** Returns the replay archive for one match by runtime match id, live matches included. */
46
+ getMatchReplay(auth: SimulatorAdminAuth, matchId: string, options?: SimulatorRequestOptions): Promise<AdminMatchReplayResponse>;
39
47
  /** Creates or returns the hidden-information replay capability for one completed game. */
40
48
  createGameReplayShare(auth: SimulatorAdminAuth, historyId: string, options?: SimulatorRequestOptions): Promise<CreateMatchHistoryReplayShareResponse>;
41
49
  /** Lists safe metadata for retained simulator diagnostic reports. */
@@ -7,6 +7,8 @@ export function createAdminResource(requester) {
7
7
  listGames: (auth, query, options) => requester.request('admin-match-list-response', adminGamesPath(query ?? {}), { headers: authHeaders(auth) }, options),
8
8
  getGameDebugBundle: (auth, historyId, options) => requester.request('admin-match-debug-bundle', `/api/admin/games/${encodeURIComponent(historyId)}/debug`, { headers: authHeaders(auth) }, options),
9
9
  getGameReplay: (auth, historyId, options) => requester.request('admin-match-replay-response', `/api/admin/games/${encodeURIComponent(historyId)}/replay`, { headers: authHeaders(auth) }, options),
10
+ getMatchDebugBundle: (auth, matchId, options) => requester.request('admin-match-debug-bundle', `/api/admin/matches/${encodeURIComponent(matchId)}/debug`, { headers: authHeaders(auth) }, options),
11
+ getMatchReplay: (auth, matchId, options) => requester.request('admin-match-replay-response', `/api/admin/matches/${encodeURIComponent(matchId)}/replay`, { headers: authHeaders(auth) }, options),
10
12
  createGameReplayShare: (auth, historyId, options) => requester.request('create-match-history-replay-share-response', `/api/admin/games/${encodeURIComponent(historyId)}/replay-shares`, { headers: authHeaders(auth), method: 'POST' }, options),
11
13
  listSimulatorReports: (auth, query, options) => requester.request('simulator-report-list-response', simulatorReportsPath(query ?? {}), { headers: authHeaders(auth) }, options),
12
14
  getSimulatorReport: (auth, reportId, options) => requester.request('simulator-report-response', `/api/admin/reports/${encodeURIComponent(reportId)}`, { headers: authHeaders(auth) }, options),
@@ -0,0 +1,10 @@
1
+ import type { WebSocketFactory } from '../internal/transport.js';
2
+ import type { SchemaValidator } from '../validators/validator.js';
3
+ import type { LimitedEventAccess, LimitedEventAccessInput, LimitedEventSession } from './limited-event-types.js';
4
+ /** Creates one direct Limited Event presence session. */
5
+ export declare function createLimitedEventSession(options: {
6
+ access: LimitedEventAccessInput | LimitedEventAccess;
7
+ validate?: SchemaValidator | undefined;
8
+ webSocketFactory: WebSocketFactory;
9
+ wsUrl: string;
10
+ }): LimitedEventSession;
@@ -0,0 +1,35 @@
1
+ import { seatTokenProtocols } from '../connection/url.js';
2
+ import { AuthenticatedSocketSessionController, authenticatedSocketUrl, } from '../internal/authenticated-socket-session.js';
3
+ import { simulatorSocketErrorPayload } from '../internal/errors.js';
4
+ /** Creates one direct Limited Event presence session. */
5
+ export function createLimitedEventSession(options) {
6
+ return new LimitedEventSessionController(options);
7
+ }
8
+ class LimitedEventSessionController extends AuthenticatedSocketSessionController {
9
+ /** Creates one seat-token authenticated session controller. */
10
+ constructor(options) {
11
+ super({
12
+ messageSchema: 'limited-event-server-message',
13
+ playerToken: options.access.seatToken,
14
+ socketError: info => simulatorSocketErrorPayload(info),
15
+ socketUrl: authenticatedSocketUrl(options.wsUrl, `limited-events/${encodeURIComponent(options.access.eventId)}`),
16
+ tokenProtocols: seatTokenProtocols,
17
+ webSocketFactory: options.webSocketFactory,
18
+ ...(options.validate == null ? {} : { validate: options.validate }),
19
+ });
20
+ }
21
+ /** Routes Limited Event protocol messages into observable session events. */
22
+ handleServerMessage(message) {
23
+ switch (message.type) {
24
+ case 'snapshot':
25
+ this.setSnapshot(message.payload);
26
+ break;
27
+ case 'error':
28
+ this.pushError(message.payload);
29
+ break;
30
+ case 'pong':
31
+ this.emit('pong', message.payload);
32
+ break;
33
+ }
34
+ }
35
+ }