@bsb/base 9.0.5 → 9.1.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.
Files changed (196) hide show
  1. package/lib/base/BSBConfig.d.ts +3 -3
  2. package/lib/base/BSBConfig.js +9 -14
  3. package/lib/base/BSBConfig.js.map +1 -1
  4. package/lib/base/BSBEvents.d.ts +3 -3
  5. package/lib/base/BSBEvents.js +12 -17
  6. package/lib/base/BSBEvents.js.map +1 -1
  7. package/lib/base/BSBObservable.d.ts +4 -4
  8. package/lib/base/BSBObservable.js +17 -22
  9. package/lib/base/BSBObservable.js.map +1 -1
  10. package/lib/base/BSBService.d.ts +7 -8
  11. package/lib/base/BSBService.js +17 -22
  12. package/lib/base/BSBService.js.map +1 -1
  13. package/lib/base/BSBServiceClient.d.ts +4 -4
  14. package/lib/base/BSBServiceClient.js +12 -18
  15. package/lib/base/BSBServiceClient.js.map +1 -1
  16. package/lib/base/EventValidator.d.ts +16 -17
  17. package/lib/base/EventValidator.js +13 -34
  18. package/lib/base/EventValidator.js.map +1 -1
  19. package/lib/base/ObservableBackend.d.ts +2 -2
  20. package/lib/base/ObservableBackend.js +21 -26
  21. package/lib/base/ObservableBackend.js.map +1 -1
  22. package/lib/base/PluginConfig.d.ts +9 -5
  23. package/lib/base/PluginConfig.js +4 -11
  24. package/lib/base/PluginConfig.js.map +1 -1
  25. package/lib/base/PluginEvents.d.ts +4 -4
  26. package/lib/base/PluginEvents.js +4 -9
  27. package/lib/base/PluginEvents.js.map +1 -1
  28. package/lib/base/PluginObservable.d.ts +13 -14
  29. package/lib/base/PluginObservable.js +4 -8
  30. package/lib/base/PluginObservable.js.map +1 -1
  31. package/lib/base/ResourceContext.d.ts +1 -1
  32. package/lib/base/ResourceContext.js +1 -5
  33. package/lib/base/ResourceContext.js.map +1 -1
  34. package/lib/base/base.d.ts +3 -3
  35. package/lib/base/base.js +10 -18
  36. package/lib/base/base.js.map +1 -1
  37. package/lib/base/errorMessages.d.ts +1 -1
  38. package/lib/base/errorMessages.js +6 -11
  39. package/lib/base/errorMessages.js.map +1 -1
  40. package/lib/base/factory.d.ts +1 -1
  41. package/lib/base/factory.js +19 -22
  42. package/lib/base/factory.js.map +1 -1
  43. package/lib/base/functions.d.ts +19 -14
  44. package/lib/base/functions.js +19 -26
  45. package/lib/base/functions.js.map +1 -1
  46. package/lib/base/index.d.ts +18 -18
  47. package/lib/base/index.js +18 -37
  48. package/lib/base/index.js.map +1 -1
  49. package/lib/base/logFormatter.d.ts +1 -1
  50. package/lib/base/logFormatter.js +10 -14
  51. package/lib/base/logFormatter.js.map +1 -1
  52. package/lib/base/module-runtime.d.ts +3 -0
  53. package/lib/base/module-runtime.js +15 -0
  54. package/lib/base/module-runtime.js.map +1 -0
  55. package/lib/base/tools.d.ts +1 -1
  56. package/lib/base/tools.js +9 -13
  57. package/lib/base/tools.js.map +1 -1
  58. package/lib/cli.js +13 -12
  59. package/lib/cli.js.map +1 -1
  60. package/lib/dev.js +22 -11
  61. package/lib/dev.js.map +1 -1
  62. package/lib/index.d.ts +3 -3
  63. package/lib/index.js +3 -19
  64. package/lib/index.js.map +1 -1
  65. package/lib/interfaces/events.d.ts +2 -2
  66. package/lib/interfaces/events.js +1 -4
  67. package/lib/interfaces/events.js.map +1 -1
  68. package/lib/interfaces/index.d.ts +13 -12
  69. package/lib/interfaces/index.js +12 -32
  70. package/lib/interfaces/index.js.map +1 -1
  71. package/lib/interfaces/logging.d.ts +3 -3
  72. package/lib/interfaces/logging.js +1 -4
  73. package/lib/interfaces/logging.js.map +1 -1
  74. package/lib/interfaces/metrics.d.ts +2 -2
  75. package/lib/interfaces/metrics.js +1 -5
  76. package/lib/interfaces/metrics.js.map +1 -1
  77. package/lib/interfaces/observable-types.d.ts +1 -1
  78. package/lib/interfaces/observable-types.js +1 -4
  79. package/lib/interfaces/observable-types.js.map +1 -1
  80. package/lib/interfaces/observable.d.ts +8 -9
  81. package/lib/interfaces/observable.js +1 -2
  82. package/lib/interfaces/observable.js.map +1 -1
  83. package/lib/interfaces/options.d.ts +15 -6
  84. package/lib/interfaces/options.js +2 -5
  85. package/lib/interfaces/options.js.map +1 -1
  86. package/lib/interfaces/plugins.d.ts +2 -2
  87. package/lib/interfaces/plugins.js +1 -4
  88. package/lib/interfaces/plugins.js.map +1 -1
  89. package/lib/interfaces/result.js +8 -18
  90. package/lib/interfaces/result.js.map +1 -1
  91. package/lib/interfaces/schema-events.d.ts +4 -19
  92. package/lib/interfaces/schema-events.js +6 -13
  93. package/lib/interfaces/schema-events.js.map +1 -1
  94. package/lib/interfaces/schema-types.d.ts +32 -352
  95. package/lib/interfaces/schema-types.js +77 -512
  96. package/lib/interfaces/schema-types.js.map +1 -1
  97. package/lib/interfaces/service.d.ts +1 -1
  98. package/lib/interfaces/service.js +1 -2
  99. package/lib/interfaces/service.js.map +1 -1
  100. package/lib/interfaces/tools.js +8 -13
  101. package/lib/interfaces/tools.js.map +1 -1
  102. package/lib/plugins/config-default/index.d.ts +16 -14
  103. package/lib/plugins/config-default/index.js +66 -49
  104. package/lib/plugins/config-default/index.js.map +1 -1
  105. package/lib/plugins/config-default/interfaces.d.ts +1 -1
  106. package/lib/plugins/config-default/interfaces.js +1 -2
  107. package/lib/plugins/config-default/interfaces.js.map +1 -1
  108. package/lib/plugins/events-default/events/broadcast.d.ts +1 -1
  109. package/lib/plugins/events-default/events/broadcast.js +2 -6
  110. package/lib/plugins/events-default/events/broadcast.js.map +1 -1
  111. package/lib/plugins/events-default/events/emit.d.ts +1 -1
  112. package/lib/plugins/events-default/events/emit.js +4 -8
  113. package/lib/plugins/events-default/events/emit.js.map +1 -1
  114. package/lib/plugins/events-default/events/emitAndReturn.d.ts +1 -1
  115. package/lib/plugins/events-default/events/emitAndReturn.js +4 -8
  116. package/lib/plugins/events-default/events/emitAndReturn.js.map +1 -1
  117. package/lib/plugins/events-default/events/emitStreamAndReceiveStream.d.ts +1 -1
  118. package/lib/plugins/events-default/events/emitStreamAndReceiveStream.js +8 -12
  119. package/lib/plugins/events-default/events/emitStreamAndReceiveStream.js.map +1 -1
  120. package/lib/plugins/events-default/events/index.d.ts +4 -4
  121. package/lib/plugins/events-default/events/index.js +4 -11
  122. package/lib/plugins/events-default/events/index.js.map +1 -1
  123. package/lib/plugins/events-default/index.d.ts +5 -5
  124. package/lib/plugins/events-default/index.js +10 -14
  125. package/lib/plugins/events-default/index.js.map +1 -1
  126. package/lib/plugins/observable-default/index.d.ts +5 -5
  127. package/lib/plugins/observable-default/index.js +8 -12
  128. package/lib/plugins/observable-default/index.js.map +1 -1
  129. package/lib/plugins/service-benchmarkify/index.d.ts +69 -185
  130. package/lib/plugins/service-benchmarkify/index.js +39 -43
  131. package/lib/plugins/service-benchmarkify/index.js.map +1 -1
  132. package/lib/plugins/service-default0/index.d.ts +73 -171
  133. package/lib/plugins/service-default0/index.js +36 -40
  134. package/lib/plugins/service-default0/index.js.map +1 -1
  135. package/lib/plugins/service-default1/client.d.ts +3 -3
  136. package/lib/plugins/service-default1/client.js +2 -6
  137. package/lib/plugins/service-default1/client.js.map +1 -1
  138. package/lib/plugins/service-default1/index.d.ts +75 -177
  139. package/lib/plugins/service-default1/index.js +41 -45
  140. package/lib/plugins/service-default1/index.js.map +1 -1
  141. package/lib/plugins/service-default2/index.d.ts +89 -231
  142. package/lib/plugins/service-default2/index.js +47 -51
  143. package/lib/plugins/service-default2/index.js.map +1 -1
  144. package/lib/plugins/service-default3/index.d.ts +23 -51
  145. package/lib/plugins/service-default3/index.js +16 -20
  146. package/lib/plugins/service-default3/index.js.map +1 -1
  147. package/lib/plugins/service-default4/index.d.ts +13 -27
  148. package/lib/plugins/service-default4/index.js +12 -16
  149. package/lib/plugins/service-default4/index.js.map +1 -1
  150. package/lib/schemas/config-default.json +24 -16
  151. package/lib/schemas/config-default.plugin.json +24 -16
  152. package/lib/schemas/events-default.json +1 -1
  153. package/lib/schemas/events-default.plugin.json +1 -1
  154. package/lib/schemas/observable-default.json +15 -5
  155. package/lib/schemas/observable-default.plugin.json +15 -5
  156. package/lib/schemas/service-benchmarkify.json +232 -147
  157. package/lib/schemas/service-default0.json +205 -142
  158. package/lib/schemas/service-default1.json +245 -160
  159. package/lib/schemas/service-default2.json +270 -183
  160. package/lib/schemas/service-default3.json +78 -36
  161. package/lib/schemas/service-default4.json +45 -15
  162. package/lib/scripts/bsb-client-cli.js +9 -9
  163. package/lib/scripts/bsb-client-cli.js.map +1 -1
  164. package/lib/scripts/bsb-plugin-cli.js +48 -50
  165. package/lib/scripts/bsb-plugin-cli.js.map +1 -1
  166. package/lib/scripts/export-schemas.js +8 -10
  167. package/lib/scripts/export-schemas.js.map +1 -1
  168. package/lib/scripts/extract-schemas-from-source.js +73 -56
  169. package/lib/scripts/extract-schemas-from-source.js.map +1 -1
  170. package/lib/scripts/generate-client-types.d.ts +1 -16
  171. package/lib/scripts/generate-client-types.js +120 -313
  172. package/lib/scripts/generate-client-types.js.map +1 -1
  173. package/lib/scripts/generate-plugin-json.js +9 -10
  174. package/lib/scripts/generate-plugin-json.js.map +1 -1
  175. package/lib/serviceBase/config.d.ts +4 -4
  176. package/lib/serviceBase/config.js +33 -35
  177. package/lib/serviceBase/config.js.map +1 -1
  178. package/lib/serviceBase/events.d.ts +6 -6
  179. package/lib/serviceBase/events.js +42 -44
  180. package/lib/serviceBase/events.js.map +1 -1
  181. package/lib/serviceBase/index.d.ts +7 -7
  182. package/lib/serviceBase/index.js +7 -23
  183. package/lib/serviceBase/index.js.map +1 -1
  184. package/lib/serviceBase/observable.d.ts +5 -5
  185. package/lib/serviceBase/observable.js +21 -25
  186. package/lib/serviceBase/observable.js.map +1 -1
  187. package/lib/serviceBase/plugins.d.ts +3 -3
  188. package/lib/serviceBase/plugins.js +47 -53
  189. package/lib/serviceBase/plugins.js.map +1 -1
  190. package/lib/serviceBase/serviceBase.d.ts +3 -3
  191. package/lib/serviceBase/serviceBase.js +40 -44
  192. package/lib/serviceBase/serviceBase.js.map +1 -1
  193. package/lib/serviceBase/services.d.ts +6 -6
  194. package/lib/serviceBase/services.js +21 -22
  195. package/lib/serviceBase/services.js.map +1 -1
  196. package/package.json +18 -11
@@ -1,148 +1,17 @@
1
- "use strict";
2
1
  /**
3
2
  * BSB Virtual Client Generator
4
3
  *
5
- * Generates TypeScript virtual client files from exported schema JSON files.
6
- * These client files provide typed wrappers around the event bus, enabling
7
- * type-safe cross-plugin communication without direct source imports.
8
- *
9
- * Schema sources:
10
- * 1. Local build: lib/schemas/*.json (from this project's export-schemas)
11
- * 2. Remote install: src/.bsb/schemas/*.json (from bsb client install)
12
- *
13
- * Output:
14
- * Writes TypeScript files to src/.bsb/clients/{plugin-name}.ts
15
- *
16
- * Generated clients use bsb.* builders to define BSBType schemas (same as
17
- * real plugins), then export inferred TypeScript types from those schemas.
18
- */
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.generateClientTypes = main;
21
- const fs = require("fs");
22
- const path = require("path");
23
- /**
24
- * Convert JSON Schema to a bsb.* builder call string.
25
- * Reconstructs the BSBType builder expression from exported JSON Schema.
4
+ * Generates TypeScript virtual client files from exported AnyVali schema documents.
26
5
  */
27
- function jsonSchemaToBsbCode(schema) {
28
- // Handle enum
29
- if (schema.enum) {
30
- const values = schema.enum.map((v) => JSON.stringify(v)).join(', ');
31
- const desc = schema.description ? `, ${JSON.stringify(schema.description)}` : '';
32
- return `bsb.enum([${values}]${desc})`;
33
- }
34
- // Handle oneOf (union types / void)
35
- if (schema.oneOf && Array.isArray(schema.oneOf)) {
36
- if (schema.oneOf.length === 0) {
37
- // void or unknown
38
- if (schema.description === 'void') {
39
- return 'bsb.void()';
40
- }
41
- return `bsb.unknown(${schema.description ? JSON.stringify(schema.description) : ''})`;
42
- }
43
- const types = schema.oneOf.map((s) => jsonSchemaToBsbCode(s)).join(', ');
44
- const desc = schema.description ? `, ${JSON.stringify(schema.description)}` : '';
45
- return `bsb.union([${types}]${desc})`;
46
- }
47
- // Handle array type (can be string or string[])
48
- const types = Array.isArray(schema.type) ? schema.type : [schema.type];
49
- const type = types[0];
50
- switch (type) {
51
- case 'string': {
52
- // Check format for specialized types
53
- if (schema.format === 'uuid') {
54
- return `bsb.uuid(${schema.description ? JSON.stringify(schema.description) : ''})`;
55
- }
56
- if (schema.format === 'datetime') {
57
- return `bsb.datetime(${schema.description ? JSON.stringify(schema.description) : ''})`;
58
- }
59
- if (schema.format === 'email') {
60
- return `bsb.email(${schema.description ? JSON.stringify(schema.description) : ''})`;
61
- }
62
- if (schema.format === 'uri' || schema.format === 'url') {
63
- return `bsb.uri(${schema.description ? JSON.stringify(schema.description) : ''})`;
64
- }
65
- // Generic string
66
- const opts = [];
67
- if (schema.minLength !== undefined)
68
- opts.push(`min: ${schema.minLength}`);
69
- if (schema.maxLength !== undefined)
70
- opts.push(`max: ${schema.maxLength}`);
71
- if (schema.pattern)
72
- opts.push(`pattern: ${JSON.stringify(schema.pattern)}`);
73
- if (schema.description)
74
- opts.push(`description: ${JSON.stringify(schema.description)}`);
75
- if (opts.length === 0)
76
- return 'bsb.string()';
77
- return `bsb.string({ ${opts.join(', ')} })`;
78
- }
79
- case 'number':
80
- case 'integer': {
81
- const format = schema.format;
82
- const opts = [];
83
- if (schema.description)
84
- opts.push(`description: ${JSON.stringify(schema.description)}`);
85
- const optsStr = opts.length > 0 ? `{ ${opts.join(', ')} }` : '';
86
- if (format === 'int32')
87
- return `bsb.int32(${optsStr ? optsStr : ''})`;
88
- if (format === 'int64')
89
- return `bsb.int64(${optsStr ? optsStr : ''})`;
90
- if (format === 'float')
91
- return `bsb.float(${optsStr ? optsStr : ''})`;
92
- // Default: number (double)
93
- return `bsb.number(${optsStr ? optsStr : ''})`;
94
- }
95
- case 'boolean':
96
- return `bsb.boolean(${schema.description ? JSON.stringify(schema.description) : ''})`;
97
- case 'array': {
98
- const itemsCode = schema.items ? jsonSchemaToBsbCode(schema.items) : 'bsb.unknown()';
99
- const opts = [];
100
- if (schema.minItems !== undefined)
101
- opts.push(`min: ${schema.minItems}`);
102
- if (schema.maxItems !== undefined)
103
- opts.push(`max: ${schema.maxItems}`);
104
- if (schema.description)
105
- opts.push(`description: ${JSON.stringify(schema.description)}`);
106
- if (opts.length === 0)
107
- return `bsb.array(${itemsCode})`;
108
- return `bsb.array(${itemsCode}, { ${opts.join(', ')} })`;
109
- }
110
- case 'object': {
111
- if (!schema.properties || Object.keys(schema.properties).length === 0) {
112
- return `bsb.object({}, ${schema.description ? JSON.stringify(schema.description) : "''"})`;
113
- }
114
- const required = schema.required || [];
115
- const propLines = [];
116
- for (const [key, propSchema] of Object.entries(schema.properties)) {
117
- const propCode = jsonSchemaToBsbCode(propSchema);
118
- const isOptional = !required.includes(key);
119
- if (isOptional) {
120
- propLines.push(` ${key}: optional(${propCode})`);
121
- }
122
- else {
123
- propLines.push(` ${key}: ${propCode}`);
124
- }
125
- }
126
- const desc = schema.description ? `, ${JSON.stringify(schema.description)}` : '';
127
- return `bsb.object({\n${propLines.join(',\n')}\n }${desc})`;
128
- }
129
- default:
130
- return 'bsb.unknown()';
131
- }
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { isMainModule } from '../base/module-runtime.js';
9
+ function anyValiDocumentToCode(document) {
10
+ return `av.importSchema(${JSON.stringify(document)} as av.AnyValiDocument)`;
132
11
  }
133
- /**
134
- * Convert event name to camelCase method name.
135
- * e.g., 'todo.create' → 'todoCreate', 'todo-created' → 'todoCreated'
136
- */
137
12
  function eventNameToMethodName(eventName) {
138
- return eventName
139
- .replace(/[.-](\w)/g, (_, c) => c.toUpperCase());
13
+ return eventName.replace(/[.-](\w)/g, (_, c) => c.toUpperCase());
140
14
  }
141
- /**
142
- * Convert plugin key/ID to PascalCase client class name.
143
- * Strip 'service-' prefix, remove non-alphanumeric chars, PascalCase, append 'Client'.
144
- * e.g., 'service-todo' → 'TodoClient', 'bsb-registry' → 'BsbRegistryClient'
145
- */
146
15
  function pluginNameToClassName(pluginId) {
147
16
  let name = pluginId;
148
17
  if (name.startsWith('service-')) {
@@ -151,58 +20,41 @@ function pluginNameToClassName(pluginId) {
151
20
  const pascal = name
152
21
  .replace(/[^a-zA-Z0-9-]/g, '')
153
22
  .split('-')
154
- .filter(part => part.length > 0)
155
- .map(part => part.charAt(0).toUpperCase() + part.slice(1))
23
+ .filter((part) => part.length > 0)
24
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
156
25
  .join('');
157
- return pascal + 'Client';
26
+ return `${pascal}Client`;
158
27
  }
159
- /**
160
- * Generate a const name from event name and role.
161
- * e.g., 'todo.create' + 'Input' → '_todoCreateInput'
162
- */
163
28
  function eventNameToConstName(eventName, suffix) {
164
- const camel = eventNameToMethodName(eventName);
165
- return '_' + camel + suffix;
29
+ return `_${eventNameToMethodName(eventName)}${suffix}`;
166
30
  }
167
- /**
168
- * Flip mapping for client method generation.
169
- * Service's onReturnableEvents → client emits returnable events, etc.
170
- */
171
31
  const FLIP_MAP = {
172
- 'emitEvents': 'onEvents',
173
- 'onEvents': 'emitEvents',
174
- 'emitReturnableEvents': 'onReturnableEvents',
175
- 'onReturnableEvents': 'emitReturnableEvents',
176
- 'emitBroadcast': 'onBroadcast',
177
- 'onBroadcast': 'emitBroadcast',
32
+ emitEvents: 'onEvents',
33
+ onEvents: 'emitEvents',
34
+ emitReturnableEvents: 'onReturnableEvents',
35
+ onReturnableEvents: 'emitReturnableEvents',
36
+ emitBroadcast: 'onBroadcast',
37
+ onBroadcast: 'emitBroadcast',
178
38
  };
179
- /**
180
- * Generate virtual client TypeScript file from exported schema.
181
- * @param pluginId The plugin key/ID (from schema filename), used for class naming
182
- */
183
39
  function generateVirtualClient(schemaExport, importBase, pluginId) {
184
40
  const lines = [];
185
41
  const className = pluginNameToClassName(pluginId);
186
- // File header
187
- lines.push(`/**`);
42
+ lines.push('/**');
188
43
  lines.push(` * Auto-generated BSB virtual client for ${schemaExport.pluginName}`);
189
- lines.push(` * DO NOT EDIT - Regenerated on every build`);
44
+ lines.push(' * DO NOT EDIT - Regenerated on every build');
190
45
  lines.push(` * @version ${schemaExport.version}`);
191
- lines.push(` */`);
192
- // Import paths differ between @bsb/base (external) and self-build (relative)
46
+ lines.push(' */');
47
+ lines.push('import * as av from "@anyvali/js";');
193
48
  if (importBase === '@bsb/base') {
194
- lines.push(`import { ServiceClient, BSBService, bsb, optional } from "@bsb/base";`);
195
- lines.push(`import { createReturnableEvent, createFireAndForgetEvent, createBroadcastEvent, createEventSchemas } from "@bsb/base";`);
196
- lines.push(`import type { Observable, BSBServiceClientDefinition, EventInputType, EventOutputType } from "@bsb/base";`);
49
+ lines.push('import { ServiceClient, BSBService, createReturnableEvent, createFireAndForgetEvent, createBroadcastEvent, createEventSchemas } from "@bsb/base";');
50
+ lines.push('import type { Observable, BSBServiceClientDefinition, EventInputType, EventOutputType } from "@bsb/base";');
197
51
  }
198
52
  else {
199
- // Self-build: src/.bsb/clients/ ../../index for the main barrel
200
- lines.push(`import { ServiceClient, BSBService, bsb, optional } from "../../index";`);
201
- lines.push(`import { createReturnableEvent, createFireAndForgetEvent, createBroadcastEvent, createEventSchemas } from "../../interfaces/schema-events";`);
202
- lines.push(`import type { Observable, BSBServiceClientDefinition, EventInputType, EventOutputType } from "../../index";`);
53
+ lines.push('import { ServiceClient, BSBService } from "../../index.js";');
54
+ lines.push('import { createReturnableEvent, createFireAndForgetEvent, createBroadcastEvent, createEventSchemas } from "../../interfaces/schema-events.js";');
55
+ lines.push('import type { Observable, BSBServiceClientDefinition, EventInputType, EventOutputType } from "../../index.js";');
203
56
  }
204
57
  lines.push('');
205
- // Group events by category
206
58
  const categorizedEvents = {
207
59
  emitEvents: [],
208
60
  onEvents: [],
@@ -212,28 +64,22 @@ function generateVirtualClient(schemaExport, importBase, pluginId) {
212
64
  onBroadcast: [],
213
65
  };
214
66
  for (const [eventName, eventDef] of Object.entries(schemaExport.events)) {
215
- const def = eventDef;
216
- if (categorizedEvents[def.category]) {
217
- categorizedEvents[def.category].push({ name: eventName, def });
67
+ if (categorizedEvents[eventDef.category]) {
68
+ categorizedEvents[eventDef.category].push({ name: eventName, def: eventDef });
218
69
  }
219
70
  }
220
- // --- Generate BSBType schema consts for each event's input/output ---
221
71
  const constsDone = new Set();
222
72
  for (const events of Object.values(categorizedEvents)) {
223
73
  for (const { name, def } of events) {
224
- // Input const
225
74
  const inputConst = eventNameToConstName(name, 'Schema');
226
75
  if (!constsDone.has(inputConst)) {
227
- const bsbCode = jsonSchemaToBsbCode(def.inputSchema);
228
- lines.push(`const ${inputConst} = ${bsbCode};`);
76
+ lines.push(`const ${inputConst} = ${anyValiDocumentToCode(def.inputSchema)};`);
229
77
  constsDone.add(inputConst);
230
78
  }
231
- // Output const (only for returnable events)
232
79
  if (def.type === 'returnable' && def.outputSchema) {
233
80
  const outputConst = eventNameToConstName(name, 'OutputSchema');
234
81
  if (!constsDone.has(outputConst)) {
235
- const bsbCode = jsonSchemaToBsbCode(def.outputSchema);
236
- lines.push(`const ${outputConst} = ${bsbCode};`);
82
+ lines.push(`const ${outputConst} = ${anyValiDocumentToCode(def.outputSchema)};`);
237
83
  constsDone.add(outputConst);
238
84
  }
239
85
  }
@@ -242,58 +88,53 @@ function generateVirtualClient(schemaExport, importBase, pluginId) {
242
88
  if (constsDone.size > 0) {
243
89
  lines.push('');
244
90
  }
245
- // --- Generate Event Schemas using createEventSchemas ---
246
- lines.push(`// --- Event Schemas (typed, follows ServiceClientEventSchemas flip) ---`);
247
- lines.push(`const _EventSchemas = createEventSchemas({`);
91
+ lines.push('const _EventSchemas = createEventSchemas({');
248
92
  for (const [category, events] of Object.entries(categorizedEvents)) {
249
93
  if (events.length === 0)
250
94
  continue;
251
95
  lines.push(` ${category}: {`);
252
96
  for (const { name, def } of events) {
253
97
  const inputConst = eventNameToConstName(name, 'Schema');
254
- const desc = def.description ? JSON.stringify(def.description) : undefined;
98
+ const descriptionArg = def.description !== undefined ? `, ${JSON.stringify(def.description)}` : '';
255
99
  if (def.type === 'returnable') {
256
100
  const outputConst = eventNameToConstName(name, 'OutputSchema');
257
- const descArg = desc ? `, ${desc}` : '';
258
- const timeoutArg = def.defaultTimeout !== undefined ? `, ${def.defaultTimeout}` : '';
259
- // If we have timeout but no desc, we need desc placeholder
260
- if (def.defaultTimeout !== undefined && !desc) {
261
- lines.push(` '${name}': createReturnableEvent(${inputConst}, ${outputConst}, undefined, ${def.defaultTimeout}),`);
101
+ if (def.defaultTimeout !== undefined) {
102
+ if (def.description !== undefined) {
103
+ lines.push(` '${name}': createReturnableEvent(${inputConst}, ${outputConst}, ${JSON.stringify(def.description)}, ${def.defaultTimeout}),`);
104
+ }
105
+ else {
106
+ lines.push(` '${name}': createReturnableEvent(${inputConst}, ${outputConst}, undefined, ${def.defaultTimeout}),`);
107
+ }
262
108
  }
263
109
  else {
264
- lines.push(` '${name}': createReturnableEvent(${inputConst}, ${outputConst}${descArg}${timeoutArg}),`);
110
+ lines.push(` '${name}': createReturnableEvent(${inputConst}, ${outputConst}${descriptionArg}),`);
265
111
  }
266
112
  }
267
113
  else if (def.type === 'broadcast') {
268
- const descArg = desc ? `, ${desc}` : '';
269
- lines.push(` '${name}': createBroadcastEvent(${inputConst}${descArg}),`);
114
+ lines.push(` '${name}': createBroadcastEvent(${inputConst}${descriptionArg}),`);
270
115
  }
271
116
  else {
272
- const descArg = desc ? `, ${desc}` : '';
273
- lines.push(` '${name}': createFireAndForgetEvent(${inputConst}${descArg}),`);
117
+ lines.push(` '${name}': createFireAndForgetEvent(${inputConst}${descriptionArg}),`);
274
118
  }
275
119
  }
276
- lines.push(` },`);
120
+ lines.push(' },');
277
121
  }
278
- lines.push(`});`);
122
+ lines.push('});');
279
123
  lines.push('');
280
- // --- Export inferred types ---
281
- lines.push(`// --- Exported Types (inferred from BSBType schemas) ---`);
124
+ lines.push('// These resolve to broad types because the source of truth is a portable AnyVali document.');
282
125
  const typesDone = new Set();
283
126
  for (const events of Object.values(categorizedEvents)) {
284
127
  for (const { name, def } of events) {
285
128
  const camel = eventNameToMethodName(name);
286
129
  const typeName = camel.charAt(0).toUpperCase() + camel.slice(1);
287
- // Input type
288
- const inputTypeName = typeName + 'Input';
130
+ const inputTypeName = `${typeName}Input`;
289
131
  if (!typesDone.has(inputTypeName)) {
290
132
  const inputConst = eventNameToConstName(name, 'Schema');
291
133
  lines.push(`export type ${inputTypeName} = EventInputType<{ input: typeof ${inputConst} }>;`);
292
134
  typesDone.add(inputTypeName);
293
135
  }
294
- // Output type (returnable only)
295
136
  if (def.type === 'returnable' && def.outputSchema) {
296
- const outputTypeName = typeName + 'Output';
137
+ const outputTypeName = `${typeName}Output`;
297
138
  if (!typesDone.has(outputTypeName)) {
298
139
  const outputConst = eventNameToConstName(name, 'OutputSchema');
299
140
  lines.push(`export type ${outputTypeName} = EventOutputType<{ output: typeof ${outputConst} }>;`);
@@ -303,97 +144,83 @@ function generateVirtualClient(schemaExport, importBase, pluginId) {
303
144
  }
304
145
  }
305
146
  lines.push('');
306
- // --- Internal Plugin Reference ---
307
- lines.push(`// --- Internal: Plugin reference for ServiceClient wiring ---`);
308
- lines.push(`const _PLUGIN_CLIENT: BSBServiceClientDefinition = {`);
147
+ lines.push('const _PLUGIN_CLIENT: BSBServiceClientDefinition = {');
309
148
  lines.push(` name: "${pluginId}",`);
310
- lines.push(`};`);
149
+ lines.push('};');
311
150
  lines.push('');
312
- lines.push(`class _PluginRef {`);
313
- lines.push(` static PLUGIN_CLIENT = _PLUGIN_CLIENT;`);
314
- lines.push(`}`);
151
+ lines.push('class _PluginRef {');
152
+ lines.push(' static PLUGIN_CLIENT = _PLUGIN_CLIENT;');
153
+ lines.push('}');
315
154
  lines.push('');
316
- // --- Exported Client Class ---
317
- lines.push(`// --- Exported Client ---`);
318
155
  lines.push(`export default class ${className} extends ServiceClient<any, typeof _EventSchemas, typeof _PluginRef> {`);
319
- lines.push(` constructor(context: BSBService) {`);
320
- lines.push(` super(_PluginRef, context);`);
321
- lines.push(` }`);
322
- // Generate methods for each event (flip categories for client perspective)
156
+ lines.push(' constructor(context: BSBService) {');
157
+ lines.push(' super(_PluginRef, context);');
158
+ lines.push(' }');
323
159
  for (const [category, events] of Object.entries(categorizedEvents)) {
324
- if (events.length === 0)
325
- continue;
326
160
  const clientCategory = FLIP_MAP[category];
327
161
  if (!clientCategory)
328
162
  continue;
329
163
  for (const { name, def } of events) {
330
164
  const methodName = eventNameToMethodName(name);
331
- const camel = eventNameToMethodName(name);
332
- const typeName = camel.charAt(0).toUpperCase() + camel.slice(1);
333
- const inputTypeName = typeName + 'Input';
165
+ const typeName = methodName.charAt(0).toUpperCase() + methodName.slice(1);
166
+ const inputTypeName = `${typeName}Input`;
334
167
  const description = def.description || name;
335
168
  lines.push('');
336
- switch (clientCategory) {
337
- case 'emitEvents': {
338
- lines.push(` /** ${description} */`);
339
- lines.push(` async ${methodName}(obs: Observable, input: ${inputTypeName}): Promise<void> {`);
340
- lines.push(` await this.events.emitEvent("${name}", obs, input);`);
341
- lines.push(` }`);
342
- break;
343
- }
344
- case 'emitReturnableEvents': {
345
- const outputTypeName = typeName + 'Output';
346
- const timeout = def.defaultTimeout ?? 5;
347
- lines.push(` /** ${description} (default timeout: ${timeout}s) */`);
348
- lines.push(` async ${methodName}(obs: Observable, input: ${inputTypeName}, timeout: number = ${timeout}): Promise<${outputTypeName}> {`);
349
- lines.push(` return this.events.emitEventAndReturn("${name}", obs, input, timeout);`);
350
- lines.push(` }`);
351
- break;
352
- }
353
- case 'emitBroadcast': {
354
- const emitMethodName = 'emit' + methodName.charAt(0).toUpperCase() + methodName.slice(1);
355
- lines.push(` /** ${description} */`);
356
- lines.push(` async ${emitMethodName}(obs: Observable, input: ${inputTypeName}): Promise<void> {`);
357
- lines.push(` await this.events.emitBroadcast("${name}", obs, input);`);
358
- lines.push(` }`);
359
- break;
360
- }
361
- case 'onEvents': {
362
- const onMethodName = 'on' + methodName.charAt(0).toUpperCase() + methodName.slice(1);
363
- lines.push(` /** ${description} */`);
364
- lines.push(` async ${onMethodName}(obs: Observable, handler: (handlerObs: Observable, input: ${inputTypeName}) => Promise<void>): Promise<void> {`);
365
- lines.push(` await this.events.onEvent("${name}", obs, handler);`);
366
- lines.push(` }`);
367
- break;
368
- }
369
- case 'onReturnableEvents': {
370
- const outputTypeName = typeName + 'Output';
371
- const onMethodName = 'on' + methodName.charAt(0).toUpperCase() + methodName.slice(1);
372
- lines.push(` /** ${description} */`);
373
- lines.push(` async ${onMethodName}(obs: Observable, handler: (handlerObs: Observable, input: ${inputTypeName}) => Promise<${outputTypeName}>): Promise<void> {`);
374
- lines.push(` await this.events.onReturnableEvent("${name}", obs, handler);`);
375
- lines.push(` }`);
376
- break;
377
- }
378
- case 'onBroadcast': {
379
- const onMethodName = 'on' + methodName.charAt(0).toUpperCase() + methodName.slice(1);
380
- lines.push(` /** ${description} */`);
381
- lines.push(` async ${onMethodName}(obs: Observable, handler: (handlerObs: Observable, input: ${inputTypeName}) => Promise<void>): Promise<void> {`);
382
- lines.push(` await this.events.onBroadcast("${name}", obs, handler);`);
383
- lines.push(` }`);
384
- break;
385
- }
169
+ if (clientCategory === 'emitEvents') {
170
+ lines.push(` /** ${description} */`);
171
+ lines.push(` async ${methodName}(obs: Observable, input: ${inputTypeName}): Promise<void> {`);
172
+ lines.push(` await this.events.emitEvent("${name}", obs, input);`);
173
+ lines.push(' }');
174
+ continue;
175
+ }
176
+ if (clientCategory === 'emitReturnableEvents') {
177
+ const outputTypeName = `${typeName}Output`;
178
+ const timeout = def.defaultTimeout ?? 5;
179
+ lines.push(` /** ${description} (default timeout: ${timeout}s) */`);
180
+ lines.push(` async ${methodName}(obs: Observable, input: ${inputTypeName}, timeout: number = ${timeout}): Promise<${outputTypeName}> {`);
181
+ lines.push(` return this.events.emitEventAndReturn("${name}", obs, input, timeout);`);
182
+ lines.push(' }');
183
+ continue;
184
+ }
185
+ if (clientCategory === 'emitBroadcast') {
186
+ const emitMethodName = `emit${methodName.charAt(0).toUpperCase()}${methodName.slice(1)}`;
187
+ lines.push(` /** ${description} */`);
188
+ lines.push(` async ${emitMethodName}(obs: Observable, input: ${inputTypeName}): Promise<void> {`);
189
+ lines.push(` await this.events.emitBroadcast("${name}", obs, input);`);
190
+ lines.push(' }');
191
+ continue;
192
+ }
193
+ if (clientCategory === 'onEvents') {
194
+ const onMethodName = `on${methodName.charAt(0).toUpperCase()}${methodName.slice(1)}`;
195
+ lines.push(` /** ${description} */`);
196
+ lines.push(` async ${onMethodName}(obs: Observable, handler: (handlerObs: Observable, input: ${inputTypeName}) => Promise<void>): Promise<void> {`);
197
+ lines.push(` await this.events.onEvent("${name}", obs, handler);`);
198
+ lines.push(' }');
199
+ continue;
200
+ }
201
+ if (clientCategory === 'onReturnableEvents') {
202
+ const outputTypeName = `${typeName}Output`;
203
+ const onMethodName = `on${methodName.charAt(0).toUpperCase()}${methodName.slice(1)}`;
204
+ lines.push(` /** ${description} */`);
205
+ lines.push(` async ${onMethodName}(obs: Observable, handler: (handlerObs: Observable, input: ${inputTypeName}) => Promise<${outputTypeName}>): Promise<void> {`);
206
+ lines.push(` await this.events.onReturnableEvent("${name}", obs, handler);`);
207
+ lines.push(' }');
208
+ continue;
209
+ }
210
+ if (clientCategory === 'onBroadcast') {
211
+ const onMethodName = `on${methodName.charAt(0).toUpperCase()}${methodName.slice(1)}`;
212
+ lines.push(` /** ${description} */`);
213
+ lines.push(` async ${onMethodName}(obs: Observable, handler: (handlerObs: Observable, input: ${inputTypeName}) => Promise<void>): Promise<void> {`);
214
+ lines.push(` await this.events.onBroadcast("${name}", obs, handler);`);
215
+ lines.push(' }');
386
216
  }
387
217
  }
388
218
  }
389
- lines.push(`}`);
219
+ lines.push('}');
390
220
  lines.push(`export { ${className} };`);
391
221
  lines.push('');
392
222
  return lines.join('\n');
393
223
  }
394
- /**
395
- * Process schema files from a directory and generate virtual clients.
396
- */
397
224
  function processSchemaDirectory(schemasDir, clientsDir, importBase, skipPlugins) {
398
225
  let generated = 0;
399
226
  let errors = 0;
@@ -401,26 +228,20 @@ function processSchemaDirectory(schemasDir, clientsDir, importBase, skipPlugins)
401
228
  return { generated, errors };
402
229
  }
403
230
  const schemaFiles = fs.readdirSync(schemasDir)
404
- .filter(file => file.endsWith('.json') && !file.endsWith('.plugin.json'));
231
+ .filter((file) => file.endsWith('.json') && !file.endsWith('.plugin.json'));
405
232
  for (const schemaFile of schemaFiles) {
406
233
  try {
407
234
  const pluginId = path.basename(schemaFile, '.json');
408
- // Skip if already processed from a higher-priority source
409
- if (skipPlugins && skipPlugins.has(pluginId)) {
235
+ if (skipPlugins?.has(pluginId)) {
410
236
  continue;
411
237
  }
412
238
  const schemaPath = path.join(schemasDir, schemaFile);
413
- const schemaContent = fs.readFileSync(schemaPath, 'utf-8');
414
- const schemaExport = JSON.parse(schemaContent);
415
- // Skip schemas with no events
239
+ const schemaExport = JSON.parse(fs.readFileSync(schemaPath, 'utf-8'));
416
240
  if (!schemaExport.events || Object.keys(schemaExport.events).length === 0) {
417
241
  continue;
418
242
  }
419
- // Generate virtual client (use pluginId from filename, not pluginName from schema)
420
243
  const clientCode = generateVirtualClient(schemaExport, importBase, pluginId);
421
- // Write to file
422
- const outputPath = path.join(clientsDir, `${pluginId}.ts`);
423
- fs.writeFileSync(outputPath, clientCode, 'utf-8');
244
+ fs.writeFileSync(path.join(clientsDir, `${pluginId}.ts`), clientCode, 'utf-8');
424
245
  // eslint-disable-next-line no-console
425
246
  console.log(` Generated virtual client for ${pluginId} (v${schemaExport.version})`);
426
247
  generated++;
@@ -433,11 +254,6 @@ function processSchemaDirectory(schemasDir, clientsDir, importBase, skipPlugins)
433
254
  }
434
255
  return { generated, errors };
435
256
  }
436
- /**
437
- * Ensure the project's .gitignore contains the src/.bsb/ entry.
438
- * Automatically adds it if missing, so generated virtual clients
439
- * are never accidentally committed.
440
- */
441
257
  function ensureGitignore(projectRoot) {
442
258
  const gitignorePath = path.join(projectRoot, '.gitignore');
443
259
  const bsbDir = path.join(projectRoot, 'src', '.bsb');
@@ -449,7 +265,7 @@ function ensureGitignore(projectRoot) {
449
265
  if (fs.existsSync(gitignorePath)) {
450
266
  const content = fs.readFileSync(gitignorePath, 'utf-8');
451
267
  const lines = content.split(/\r?\n/);
452
- const alreadyIgnored = lines.some(line => {
268
+ const alreadyIgnored = lines.some((line) => {
453
269
  const trimmed = line.trim();
454
270
  return trimmed === relativeBsbDir ||
455
271
  trimmed === relativeBsbDir.replace(/\/$/, '') ||
@@ -460,27 +276,22 @@ function ensureGitignore(projectRoot) {
460
276
  });
461
277
  if (!alreadyIgnored) {
462
278
  const newline = content.endsWith('\n') ? '' : '\n';
463
- fs.writeFileSync(gitignorePath, content + newline + relativeBsbDir + '\n', 'utf-8');
279
+ fs.writeFileSync(gitignorePath, `${content}${newline}${relativeBsbDir}\n`, 'utf-8');
464
280
  // eslint-disable-next-line no-console
465
281
  console.log(` Added '${relativeBsbDir}' to .gitignore`);
466
282
  }
283
+ return;
467
284
  }
468
- else {
469
- fs.writeFileSync(gitignorePath, relativeBsbDir + '\n', 'utf-8');
470
- // eslint-disable-next-line no-console
471
- console.log(` Created .gitignore with '${relativeBsbDir}'`);
472
- }
285
+ fs.writeFileSync(gitignorePath, `${relativeBsbDir}\n`, 'utf-8');
286
+ // eslint-disable-next-line no-console
287
+ console.log(` Created .gitignore with '${relativeBsbDir}'`);
473
288
  }
474
289
  catch {
475
- // Non-fatal: don't fail the build if we can't update .gitignore
290
+ // Non-fatal.
476
291
  }
477
292
  }
478
- /**
479
- * Main generation function.
480
- */
481
293
  async function main() {
482
294
  const projectRoot = process.cwd();
483
- // Detect if we're in @bsb/base itself or a plugin project
484
295
  const packageJsonPath = path.join(projectRoot, 'package.json');
485
296
  let importBase = '@bsb/base';
486
297
  if (fs.existsSync(packageJsonPath)) {
@@ -491,20 +302,16 @@ async function main() {
491
302
  }
492
303
  }
493
304
  catch {
494
- // ignore parse errors
305
+ // Ignore package.json parse errors for this helper script.
495
306
  }
496
307
  }
497
- // Create output directory
498
308
  const clientsDir = path.join(projectRoot, 'src', '.bsb', 'clients');
499
309
  if (!fs.existsSync(clientsDir)) {
500
310
  fs.mkdirSync(clientsDir, { recursive: true });
501
311
  }
502
- // Ensure .gitignore covers the generated directory
503
312
  ensureGitignore(projectRoot);
504
313
  let totalGenerated = 0;
505
314
  let totalErrors = 0;
506
- // Schema source: src/.bsb/schemas/*.json
507
- // These are extracted from TS source pre-compilation, or installed via `bsb client install`
508
315
  const bsbSchemasDir = path.join(projectRoot, 'src', '.bsb', 'schemas');
509
316
  if (fs.existsSync(bsbSchemasDir)) {
510
317
  // eslint-disable-next-line no-console
@@ -526,12 +333,12 @@ async function main() {
526
333
  process.exit(1);
527
334
  }
528
335
  }
529
- // Run if called directly
530
- if (require.main === module) {
531
- main().catch(error => {
336
+ if (isMainModule(import.meta.url)) {
337
+ main().catch((error) => {
532
338
  // eslint-disable-next-line no-console
533
339
  console.error('Fatal error during client generation:', error);
534
340
  process.exit(1);
535
341
  });
536
342
  }
343
+ export { main as generateClientTypes };
537
344
  //# sourceMappingURL=generate-client-types.js.map