bonescript-compiler 0.9.0 → 0.11.0
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/dist/cli.js +79 -8
- package/dist/cli.js.map +1 -1
- package/dist/emit_colyseus.d.ts +26 -0
- package/dist/emit_colyseus.js +812 -0
- package/dist/emit_colyseus.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lowering.js +77 -8
- package/dist/lowering.js.map +1 -1
- package/package.json +4 -2
- package/src/cli.ts +87 -9
- package/src/emit_colyseus.ts +867 -0
- package/src/index.ts +1 -0
- package/src/lowering.ts +81 -9
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export { FullEmitter } from "./emit_full";
|
|
|
18
18
|
export { NakamaEmitter } from "./emit_nakama";
|
|
19
19
|
export { PrismaEmitter } from "./emit_prisma";
|
|
20
20
|
export { SqliteEmitter } from "./emit_sqlite";
|
|
21
|
+
export { ColyseusEmitter } from "./emit_colyseus";
|
|
21
22
|
export type { NakamaEmittedFile } from "./emit_nakama";
|
|
22
23
|
export type { EmittedFile } from "./emitter";
|
|
23
24
|
export { Verifier } from "./verifier";
|
package/src/lowering.ts
CHANGED
|
@@ -17,6 +17,27 @@ function toSnakeCase(s: string): string {
|
|
|
17
17
|
return s.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Extract the entity name from a `participants:` type expression.
|
|
22
|
+
*
|
|
23
|
+
* - `Player` → "Player"
|
|
24
|
+
* - `set<Player>` → "Player"
|
|
25
|
+
* - `list<Player>` → "Player"
|
|
26
|
+
* - `optional<Player>` → "Player"
|
|
27
|
+
* - anything else → null
|
|
28
|
+
*/
|
|
29
|
+
function extractParticipantEntity(t: AST.TypeExprNode | null): string | null {
|
|
30
|
+
if (!t) return null;
|
|
31
|
+
if (t.kind === "EntityRefType") return t.name;
|
|
32
|
+
if (t.kind === "GenericType") {
|
|
33
|
+
for (const arg of t.typeArgs) {
|
|
34
|
+
const inner = extractParticipantEntity(arg);
|
|
35
|
+
if (inner) return inner;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
20
41
|
// ââ€Âۉâ€Âۉâ€Â€ Deterministic ID Generation ââ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Â€
|
|
21
42
|
|
|
22
43
|
function makeId(systemName: string, kind: string, name: string): string {
|
|
@@ -122,11 +143,30 @@ export class Lowering {
|
|
|
122
143
|
}
|
|
123
144
|
|
|
124
145
|
// Lower channels → realtime_service modules
|
|
146
|
+
// Lower channels — realtime_service modules.
|
|
147
|
+
//
|
|
148
|
+
// Capabilities tagged `sync: realtime` are routed into the channel that
|
|
149
|
+
// matches their participant entity, so each room can expose them as typed
|
|
150
|
+
// onMessage handlers. The matching rule: if any of the capability's
|
|
151
|
+
// parameters is the participant entity type (e.g. channel has
|
|
152
|
+
// `participants: set<Player>` and capability has `move(player: Player, ...)`),
|
|
153
|
+
// it gets dispatched on that room.
|
|
154
|
+
const realtimeCaps = capabilities.filter(c => c.sync === "realtime");
|
|
125
155
|
for (const channel of channels) {
|
|
126
|
-
|
|
156
|
+
const channelCaps = realtimeCaps.filter(c => {
|
|
157
|
+
const partEntityName = extractParticipantEntity(channel.participants);
|
|
158
|
+
if (!partEntityName) return false;
|
|
159
|
+
return c.params.some(p => {
|
|
160
|
+
if (p.type.kind === "EntityRefType") return p.type.name === partEntityName;
|
|
161
|
+
// Also match generic types like `set<Player>` if they happen to appear as a param
|
|
162
|
+
if (p.type.kind === "GenericType") {
|
|
163
|
+
return p.type.typeArgs.some(a => a.kind === "EntityRefType" && a.name === partEntityName);
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
modules.push(this.lowerChannel(channel, channelCaps, entities));
|
|
127
169
|
}
|
|
128
|
-
|
|
129
|
-
// Lower events
|
|
130
170
|
for (const ev of eventDecls) {
|
|
131
171
|
events.push(this.lowerEvent(ev));
|
|
132
172
|
}
|
|
@@ -516,18 +556,48 @@ export class Lowering {
|
|
|
516
556
|
|
|
517
557
|
// ââ€Âۉâ€Âۉâ€Â€ Channel Lowering ââ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Âۉâ€Â€
|
|
518
558
|
|
|
519
|
-
private lowerChannel(
|
|
559
|
+
private lowerChannel(
|
|
560
|
+
channel: AST.ChannelDeclNode,
|
|
561
|
+
realtimeCapabilities: AST.CapabilityDeclNode[] = [],
|
|
562
|
+
entities: AST.EntityDeclNode[] = [],
|
|
563
|
+
): IR.IRModule {
|
|
564
|
+
// Default lifecycle methods that every Colyseus/WebSocket channel exposes.
|
|
565
|
+
const baseMethods: IR.IRMethod[] = [
|
|
566
|
+
{ name: "connect", input: [], output: "connection", preconditions: [], effects: [], emissions: [], idempotent: false, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
|
|
567
|
+
{ name: "subscribe", input: [{ name: "topic", type: "string", nullable: false, unique: false, indexed: false, default_value: null }], output: "subscription", preconditions: [], effects: [], emissions: [], idempotent: true, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
|
|
568
|
+
{ name: "publish", input: [{ name: "message", type: "json", nullable: false, unique: false, indexed: false, default_value: null }], output: "void", preconditions: [], effects: [], emissions: [], idempotent: false, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
|
|
569
|
+
];
|
|
570
|
+
|
|
571
|
+
// Realtime capabilities lower as additional interface methods. The
|
|
572
|
+
// Colyseus emitter will turn these into typed onMessage handlers.
|
|
573
|
+
const capMethods = realtimeCapabilities.map(c => this.lowerCapability(c));
|
|
574
|
+
|
|
575
|
+
const participantEntity = extractParticipantEntity(channel.participants);
|
|
576
|
+
|
|
577
|
+
// If we have an entity reference, copy its field definitions into the
|
|
578
|
+
// channel's config so the Colyseus emitter can build a Player schema
|
|
579
|
+
// without re-resolving the AST. We only forward primitive-typed `owns:`
|
|
580
|
+
// fields — entity refs and complex types are skipped because Colyseus
|
|
581
|
+
// schemas need scalar @type decorators.
|
|
582
|
+
const participantFields: { name: string; type: string }[] = [];
|
|
583
|
+
if (participantEntity) {
|
|
584
|
+
const entity = entities.find(e => e.name === participantEntity);
|
|
585
|
+
if (entity) {
|
|
586
|
+
for (const f of entity.owns) {
|
|
587
|
+
if (f.type.kind === "PrimitiveType") {
|
|
588
|
+
participantFields.push({ name: f.name, type: f.type.name });
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
520
594
|
return {
|
|
521
595
|
id: makeId(this.systemName, "realtime_service", channel.name),
|
|
522
596
|
kind: "realtime_service",
|
|
523
597
|
name: channel.name,
|
|
524
598
|
interfaces: [{
|
|
525
599
|
name: `I${channel.name}Channel`,
|
|
526
|
-
methods: [
|
|
527
|
-
{ name: "connect", input: [], output: "connection", preconditions: [], effects: [], emissions: [], idempotent: false, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
|
|
528
|
-
{ name: "subscribe", input: [{ name: "topic", type: "string", nullable: false, unique: false, indexed: false, default_value: null }], output: "subscription", preconditions: [], effects: [], emissions: [], idempotent: true, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
|
|
529
|
-
{ name: "publish", input: [{ name: "message", type: "json", nullable: false, unique: false, indexed: false, default_value: null }], output: "void", preconditions: [], effects: [], emissions: [], idempotent: false, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
|
|
530
|
-
],
|
|
600
|
+
methods: [...baseMethods, ...capMethods],
|
|
531
601
|
}],
|
|
532
602
|
models: [],
|
|
533
603
|
events: [],
|
|
@@ -539,6 +609,8 @@ export class Lowering {
|
|
|
539
609
|
ordering: channel.ordering || "fifo",
|
|
540
610
|
persistence: channel.persistence || "none",
|
|
541
611
|
max_size: channel.maxSize || 10000,
|
|
612
|
+
...(participantEntity ? { participant_entity: participantEntity } : {}),
|
|
613
|
+
...(participantFields.length > 0 ? { participant_fields: JSON.stringify(participantFields) } : {}),
|
|
542
614
|
},
|
|
543
615
|
};
|
|
544
616
|
}
|