@walkeros/config 4.7.0-next-1790722899482 → 4.7.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/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # @walkeros/config
2
2
 
3
- ## 4.7.0-next-1790722899482
3
+ ## 4.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 64b06de: Packages with several exports, such as the GCP and AWS sources and
8
+ destinations, now publish a settings schema per export. `walkeros validate`
9
+ and MCP `package_get` use the schema of the export a step imports. Pub/Sub,
10
+ BigQuery, SNS and SQS steps pinned to this release or later are checked
11
+ instead of skipped; older pins still report a skip.
4
12
 
5
13
  ### Patch Changes
6
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walkeros/config",
3
- "version": "4.7.0-next-1790722899482",
3
+ "version": "4.7.0",
4
4
  "type": "module",
5
5
  "description": "Shared development configuration for walkerOS packages (TypeScript, ESLint, Jest, tsup)",
6
6
  "license": "MIT",
@@ -0,0 +1,129 @@
1
+ import {
2
+ assertExportMaps,
3
+ assertStepSettings,
4
+ publishedExports,
5
+ } from '../exportMaps.mjs';
6
+
7
+ const two = { destinationA: 'A', destinationB: 'B' };
8
+ const examples = { destinationA: {}, destinationB: {} };
9
+ const schemas = {
10
+ destinationA: { settings: {} },
11
+ destinationB: { settings: {}, setup: {} },
12
+ };
13
+
14
+ describe('assertExportMaps', () => {
15
+ it.each([
16
+ ['no declared exports and no maps', undefined, {}],
17
+ ['a single export and no maps', { destinationA: 'A' }, {}],
18
+ [
19
+ 'two exports with matching maps',
20
+ two,
21
+ { exportExamples: examples, exportSchemas: schemas },
22
+ ],
23
+ ])('passes for %s', (_label, declared, maps) => {
24
+ expect(() => assertExportMaps('@walkeros/x', declared, maps)).not.toThrow();
25
+ });
26
+
27
+ it.each([
28
+ [
29
+ 'exportSchemas missing',
30
+ two,
31
+ { exportExamples: examples },
32
+ '@walkeros/x: exportSchemas is missing',
33
+ ],
34
+ [
35
+ 'exportExamples missing',
36
+ two,
37
+ { exportSchemas: schemas },
38
+ '@walkeros/x: exportExamples is missing',
39
+ ],
40
+ [
41
+ 'an export missing from exportSchemas',
42
+ two,
43
+ {
44
+ exportExamples: examples,
45
+ exportSchemas: { destinationA: { settings: {} } },
46
+ },
47
+ '@walkeros/x: exportSchemas is missing export "destinationB"',
48
+ ],
49
+ [
50
+ 'an undeclared export in exportSchemas',
51
+ two,
52
+ {
53
+ exportExamples: examples,
54
+ exportSchemas: { ...schemas, destinationC: { settings: {} } },
55
+ },
56
+ '@walkeros/x: exportSchemas has undeclared export "destinationC"',
57
+ ],
58
+ [
59
+ 'an exportSchemas entry without settings',
60
+ two,
61
+ {
62
+ exportExamples: examples,
63
+ exportSchemas: { ...schemas, destinationB: { setup: {} } },
64
+ },
65
+ '@walkeros/x: exportSchemas.destinationB has no settings schema',
66
+ ],
67
+ [
68
+ 'an export missing from exportExamples',
69
+ two,
70
+ { exportExamples: { destinationA: {} }, exportSchemas: schemas },
71
+ '@walkeros/x: exportExamples is missing export "destinationB"',
72
+ ],
73
+ [
74
+ 'maps on a single-export package',
75
+ { destinationA: 'A' },
76
+ { exportSchemas: { destinationA: { settings: {} } } },
77
+ '@walkeros/x: exportSchemas is set but the package declares fewer than two exports',
78
+ ],
79
+ ])('throws for %s', (_label, declared, maps, message) => {
80
+ expect(() => assertExportMaps('@walkeros/x', declared, maps)).toThrow(
81
+ new Error(message),
82
+ );
83
+ });
84
+ });
85
+
86
+ describe('assertStepSettings', () => {
87
+ it.each(['source', 'transformer', 'destination', 'store'])(
88
+ 'throws for a %s package without schemas.settings',
89
+ (type) => {
90
+ expect(() => assertStepSettings('@walkeros/x', type, {})).toThrow(
91
+ new Error('@walkeros/x: schemas.settings is missing'),
92
+ );
93
+ },
94
+ );
95
+
96
+ it.each([
97
+ ['a destination with settings', 'destination', { settings: {} }],
98
+ ['a package without a type', undefined, {}],
99
+ ['a non-step package', 'core', {}],
100
+ ])('passes for %s', (_label, type, published) => {
101
+ expect(() =>
102
+ assertStepSettings('@walkeros/x', type, published),
103
+ ).not.toThrow();
104
+ });
105
+ });
106
+
107
+ describe('publishedExports', () => {
108
+ it.each([
109
+ [
110
+ 'a multi-export package keeps its declared exports',
111
+ { type: 'destination', exports: two },
112
+ two,
113
+ ],
114
+ [
115
+ 'a single-export package keeps its one declared export',
116
+ { type: 'destination', exports: { destinationA: 'A' } },
117
+ { destinationA: 'A' },
118
+ ],
119
+ [
120
+ 'a step package without declared exports publishes an empty list',
121
+ { type: 'source' },
122
+ {},
123
+ ],
124
+ ['a non-step package publishes no list', { type: 'core' }, undefined],
125
+ ['a package without a type publishes no list', {}, undefined],
126
+ ])('%s', (_label, walkerOS, expected) => {
127
+ expect(publishedExports(walkerOS)).toEqual(expected);
128
+ });
129
+ });
@@ -0,0 +1,78 @@
1
+ // Guards for published step metadata in dist/walkerOS.json. A package that
2
+ // declares two or more exports in package.json `walkerOS.exports` ships
3
+ // `exportExamples` and `exportSchemas` keyed by exactly those export names,
4
+ // the default export included, so a reader never falls back to another
5
+ // export's examples or settings schema.
6
+
7
+ const STEP_TYPES = ['source', 'transformer', 'destination', 'store'];
8
+
9
+ function isRecord(value) {
10
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
11
+ }
12
+
13
+ function assertKeys(packageName, mapName, map, declared) {
14
+ if (!isRecord(map)) throw new Error(`${packageName}: ${mapName} is missing`);
15
+ for (const name of declared) {
16
+ if (!(name in map))
17
+ throw new Error(`${packageName}: ${mapName} is missing export "${name}"`);
18
+ }
19
+ for (const name of Object.keys(map)) {
20
+ if (!declared.includes(name))
21
+ throw new Error(
22
+ `${packageName}: ${mapName} has undeclared export "${name}"`,
23
+ );
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Throw when the per-export maps do not match the declared exports: with two
29
+ * or more exports both maps list every export and each `exportSchemas` entry
30
+ * carries a `settings` schema; with fewer, neither map is present.
31
+ */
32
+ function assertExportMaps(packageName, declaredExports, maps) {
33
+ const declared = isRecord(declaredExports)
34
+ ? Object.keys(declaredExports)
35
+ : [];
36
+ const { exportExamples, exportSchemas } = maps;
37
+
38
+ if (declared.length < 2) {
39
+ for (const [mapName, map] of [
40
+ ['exportExamples', exportExamples],
41
+ ['exportSchemas', exportSchemas],
42
+ ]) {
43
+ if (map !== undefined)
44
+ throw new Error(
45
+ `${packageName}: ${mapName} is set but the package declares fewer than two exports`,
46
+ );
47
+ }
48
+ return;
49
+ }
50
+
51
+ assertKeys(packageName, 'exportExamples', exportExamples, declared);
52
+ assertKeys(packageName, 'exportSchemas', exportSchemas, declared);
53
+ for (const [name, entry] of Object.entries(exportSchemas)) {
54
+ if (!isRecord(entry) || !isRecord(entry.settings))
55
+ throw new Error(
56
+ `${packageName}: exportSchemas.${name} has no settings schema`,
57
+ );
58
+ }
59
+ }
60
+
61
+ /** Throw when a step package (source, transformer, destination, store) publishes no settings schema. */
62
+ function assertStepSettings(packageName, type, schemas) {
63
+ if (!STEP_TYPES.includes(type)) return;
64
+ if (!isRecord(schemas) || !isRecord(schemas.settings))
65
+ throw new Error(`${packageName}: schemas.settings is missing`);
66
+ }
67
+
68
+ /**
69
+ * The `$meta.exports` a package publishes: its declared `walkerOS.exports`,
70
+ * else an empty list for a step package, so a reader can tell a package
71
+ * without named exports from an older package that published no list.
72
+ */
73
+ function publishedExports(walkerOS) {
74
+ if (isRecord(walkerOS.exports)) return walkerOS.exports;
75
+ return STEP_TYPES.includes(walkerOS.type) ? {} : undefined;
76
+ }
77
+
78
+ export { assertExportMaps, assertStepSettings, publishedExports };
package/tsup/index.mjs CHANGED
@@ -3,6 +3,11 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
3
3
  import { resolve, join } from 'path';
4
4
  import { pathToFileURL } from 'url';
5
5
  import { assertNoPublishedCredentials } from './credentialGuard.mjs';
6
+ import {
7
+ assertExportMaps,
8
+ assertStepSettings,
9
+ publishedExports,
10
+ } from './exportMaps.mjs';
6
11
 
7
12
  const baseConfig = {
8
13
  entry: ['src/index.ts'],
@@ -17,7 +22,10 @@ const getVersion = () => {
17
22
  const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
18
23
  return pkg.version || '0.0.0';
19
24
  } catch (error) {
20
- console.warn('Could not read package.json for version injection:', error.message);
25
+ console.warn(
26
+ 'Could not read package.json for version injection:',
27
+ error.message,
28
+ );
21
29
  return '0.0.0';
22
30
  }
23
31
  };
@@ -141,7 +149,9 @@ const buildDev = (customConfig = {}) => {
141
149
  // Check the built module exists
142
150
  const devMjsPath = resolve(cwd, 'dist/dev.mjs');
143
151
  if (!existsSync(devMjsPath)) {
144
- console.warn('[buildDev] dist/dev.mjs not found, skipping walkerOS.json generation');
152
+ console.warn(
153
+ '[buildDev] dist/dev.mjs not found, skipping walkerOS.json generation',
154
+ );
145
155
  return;
146
156
  }
147
157
 
@@ -174,7 +184,8 @@ const buildDev = (customConfig = {}) => {
174
184
  : [walkerOS.platform];
175
185
  }
176
186
  if (walkerOS.renderer) meta.renderer = walkerOS.renderer;
177
- if (walkerOS.exports) meta.exports = walkerOS.exports;
187
+ const exports = publishedExports(walkerOS);
188
+ if (exports) meta.exports = exports;
178
189
 
179
190
  // Docs URL (manual, from walkerOS.docs in package.json)
180
191
  if (walkerOS.docs) meta.docs = walkerOS.docs;
@@ -182,7 +193,9 @@ const buildDev = (customConfig = {}) => {
182
193
  // Source URL (auto-derived from repository.directory)
183
194
  const repo = pkg.repository;
184
195
  if (repo?.directory) {
185
- const repoUrl = (repo.url || '').replace(/^git\+/, '').replace(/\.git$/, '');
196
+ const repoUrl = (repo.url || '')
197
+ .replace(/^git\+/, '')
198
+ .replace(/\.git$/, '');
186
199
  if (repoUrl) meta.source = `${repoUrl}/tree/main/${repo.directory}/src`;
187
200
  }
188
201
 
@@ -200,6 +213,13 @@ const buildDev = (customConfig = {}) => {
200
213
  output.exportExamples = exportExamples;
201
214
  }
202
215
 
216
+ // Multi-export packages ship schemas per export, same shape as schemas
217
+ if (devModule.exportSchemas) {
218
+ const exportSchemas = toSerializable(devModule.exportSchemas);
219
+ if (exportSchemas && Object.keys(exportSchemas).length > 0)
220
+ output.exportSchemas = exportSchemas;
221
+ }
222
+
203
223
  if (hints && Object.keys(hints).length > 0) {
204
224
  output.hints = hints;
205
225
  }
@@ -211,6 +231,13 @@ const buildDev = (customConfig = {}) => {
211
231
  hints: output.hints,
212
232
  });
213
233
 
234
+ // Per-export maps match walkerOS.exports; step packages ship settings
235
+ assertExportMaps(pkg.name, walkerOS.exports, {
236
+ exportExamples: output.exportExamples,
237
+ exportSchemas: output.exportSchemas,
238
+ });
239
+ assertStepSettings(pkg.name, walkerOS.type, schemas);
240
+
214
241
  // Validate
215
242
  if (Object.keys(schemas).length === 0) {
216
243
  console.warn('[buildDev] Warning: schemas is empty');