Skip to content

Commit 0c51504

Browse files
convention
1 parent 7561dda commit 0c51504

File tree

7 files changed

+146
-78
lines changed

7 files changed

+146
-78
lines changed

packages/cli/src/__tests__/core/auth.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ import {
44
authenticatedFetch,
55
} from '../../core/auth.js';
66

7+
// Isolate from real ~/.config/walkeros/config.json
8+
jest.mock('../../lib/config-file.js', () => ({
9+
resolveToken: () => {
10+
const token = process.env.WALKEROS_TOKEN;
11+
if (!token) return null;
12+
return { token, source: 'env' as const };
13+
},
14+
resolveAppUrl: () =>
15+
process.env.WALKEROS_APP_URL || 'https://app.walkeros.io',
16+
}));
17+
718
describe('auth', () => {
819
const originalEnv = process.env;
920

Lines changed: 47 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { registerGetPackageSchemaTool } from '../../tools/get-package-schema.js';
2+
import { fetchPackageSchema } from '@walkeros/core';
23

3-
// Mock fetch globally
4-
const mockFetch = jest.fn();
5-
global.fetch = mockFetch;
4+
jest.mock('@walkeros/core', () => ({
5+
fetchPackageSchema: jest.fn(),
6+
}));
7+
8+
const mockFetchPackageSchema = fetchPackageSchema as jest.MockedFunction<
9+
typeof fetchPackageSchema
10+
>;
611

712
function createMockServer() {
813
const tools: Record<string, { config: unknown; handler: Function }> = {};
@@ -30,41 +35,23 @@ describe('get-package-schema tool', () => {
3035
});
3136

3237
it('should fetch package.json then walkerOS.json from jsdelivr', async () => {
33-
const mockPkgJson = {
34-
name: '@walkeros/web-destination-snowplow',
38+
mockFetchPackageSchema.mockResolvedValue({
39+
packageName: '@walkeros/web-destination-snowplow',
3540
version: '0.0.12',
36-
walkerOS: {
37-
type: 'destination',
38-
platform: 'web',
39-
schema: './dist/dev/walkerOS.json',
40-
},
41-
};
42-
const mockWalkerOSJson = {
41+
type: 'destination',
42+
platform: 'web',
4343
schemas: { settings: { type: 'object', properties: {} } },
4444
examples: { mapping: {} },
45-
};
46-
47-
mockFetch
48-
.mockResolvedValueOnce({
49-
ok: true,
50-
json: () => Promise.resolve(mockPkgJson),
51-
})
52-
.mockResolvedValueOnce({
53-
ok: true,
54-
json: () => Promise.resolve(mockWalkerOSJson),
55-
});
45+
});
5646

5747
const tool = mockServer.getTool('get-package-schema');
5848
const result = await tool.handler({
5949
package: '@walkeros/web-destination-snowplow',
6050
});
6151

62-
expect(mockFetch).toHaveBeenCalledTimes(2);
63-
expect(mockFetch).toHaveBeenCalledWith(
64-
expect.stringContaining(
65-
'cdn.jsdelivr.net/npm/@walkeros/web-destination-snowplow',
66-
),
67-
expect.objectContaining({ signal: expect.any(AbortSignal) }),
52+
expect(mockFetchPackageSchema).toHaveBeenCalledWith(
53+
'@walkeros/web-destination-snowplow',
54+
{ version: undefined },
6855
);
6956

7057
const content = JSON.parse(result.content[0].text);
@@ -74,64 +61,60 @@ describe('get-package-schema tool', () => {
7461
});
7562

7663
it('should use default schema path when walkerOS field is missing', async () => {
77-
mockFetch
78-
.mockResolvedValueOnce({
79-
ok: true,
80-
json: () => Promise.resolve({ name: 'some-pkg', version: '1.0.0' }),
81-
})
82-
.mockResolvedValueOnce({
83-
ok: true,
84-
json: () => Promise.resolve({ schemas: { settings: {} } }),
85-
});
64+
mockFetchPackageSchema.mockResolvedValue({
65+
packageName: 'some-pkg',
66+
version: '1.0.0',
67+
type: undefined,
68+
platform: undefined,
69+
schemas: { settings: {} },
70+
examples: {},
71+
});
8672

8773
const tool = mockServer.getTool('get-package-schema');
88-
await tool.handler({ package: 'some-pkg' });
74+
const result = await tool.handler({ package: 'some-pkg' });
8975

90-
expect(mockFetch).toHaveBeenNthCalledWith(
91-
2,
92-
expect.stringContaining('dist/dev/walkerOS.json'),
93-
expect.objectContaining({ signal: expect.any(AbortSignal) }),
94-
);
76+
expect(mockFetchPackageSchema).toHaveBeenCalledWith('some-pkg', {
77+
version: undefined,
78+
});
79+
const content = JSON.parse(result.content[0].text);
80+
expect(content.package).toBe('some-pkg');
9581
});
9682

9783
it('should return error when package not found', async () => {
98-
mockFetch.mockResolvedValueOnce({ ok: false, status: 404 });
84+
mockFetchPackageSchema.mockRejectedValue(
85+
new Error('Package "nonexistent" not found on npm (HTTP 404)'),
86+
);
9987

10088
const tool = mockServer.getTool('get-package-schema');
10189
const result = await tool.handler({ package: 'nonexistent' });
10290
expect(result.isError).toBe(true);
10391
});
10492

10593
it('should return error when walkerOS.json not found', async () => {
106-
mockFetch
107-
.mockResolvedValueOnce({
108-
ok: true,
109-
json: () => Promise.resolve({ name: 'pkg', version: '1.0.0' }),
110-
})
111-
.mockResolvedValueOnce({ ok: false, status: 404 });
94+
mockFetchPackageSchema.mockRejectedValue(
95+
new Error('walkerOS.json not found at dist/dev/walkerOS.json (HTTP 404)'),
96+
);
11297

11398
const tool = mockServer.getTool('get-package-schema');
11499
const result = await tool.handler({ package: 'pkg' });
115100
expect(result.isError).toBe(true);
116101
});
117102

118103
it('should support version parameter', async () => {
119-
mockFetch
120-
.mockResolvedValueOnce({
121-
ok: true,
122-
json: () => Promise.resolve({ name: 'pkg', version: '2.0.0' }),
123-
})
124-
.mockResolvedValueOnce({
125-
ok: true,
126-
json: () => Promise.resolve({ schemas: {} }),
127-
});
104+
mockFetchPackageSchema.mockResolvedValue({
105+
packageName: 'pkg',
106+
version: '2.0.0',
107+
type: undefined,
108+
platform: undefined,
109+
schemas: {},
110+
examples: {},
111+
});
128112

129113
const tool = mockServer.getTool('get-package-schema');
130114
await tool.handler({ package: 'pkg', version: '2.0.0' });
131115

132-
expect(mockFetch).toHaveBeenCalledWith(
133-
expect.stringContaining('pkg@2.0.0'),
134-
expect.objectContaining({ signal: expect.any(AbortSignal) }),
135-
);
116+
expect(mockFetchPackageSchema).toHaveBeenCalledWith('pkg', {
117+
version: '2.0.0',
118+
});
136119
});
137120
});

skills/walkeros-create-destination/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ discovery.
227227
"platform": "web",
228228
"schema": "./dist/dev/walkerOS.json"
229229
},
230-
"keywords": ["walkeros", "walkeros-destination", ...]
230+
"keywords": ["walkerOS", "walkerOS-destination", ...]
231231
}
232232
```
233233

@@ -249,7 +249,7 @@ time.
249249
- [ ] `walkerOS` field in package.json with `type: "destination"` and `platform`
250250
- [ ] `buildDev()` in tsup.config.ts
251251
- [ ] Build generates `dist/dev/walkerOS.json`
252-
- [ ] Keywords include `walkeros` and `walkeros-destination`
252+
- [ ] Keywords include `walkerOS` and `walkerOS-destination`
253253

254254
---
255255

skills/walkeros-create-source/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ discovery.
237237
"platform": "web",
238238
"schema": "./dist/dev/walkerOS.json"
239239
},
240-
"keywords": ["walkeros", "walkeros-source", ...]
240+
"keywords": ["walkerOS", "walkerOS-source", ...]
241241
}
242242
```
243243

@@ -261,7 +261,7 @@ time.
261261
- [ ] `walkerOS` field in package.json with `type: "source"` and `platform`
262262
- [ ] `buildDev()` in tsup.config.ts
263263
- [ ] Build generates `dist/dev/walkerOS.json`
264-
- [ ] Keywords include `walkeros` and `walkeros-source`
264+
- [ ] Keywords include `walkerOS` and `walkerOS-source`
265265

266266
---
267267

skills/walkeros-create-transformer/SKILL.md

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Before starting, read these skills:
1919
structure
2020
- [testing-strategy](../walkeros-testing-strategy/SKILL.md) - How to test
2121
- [writing-documentation](../walkeros-writing-documentation/SKILL.md) -
22-
Documentation standards (for Phase 6)
22+
Documentation standards (for Phase 7)
2323

2424
## Transformer Categories
2525

@@ -35,9 +35,10 @@ Before starting, read these skills:
3535
1. Research → Understand use case (validate/enrich/redact)
3636
2. Examples → Create event before/after examples FIRST
3737
3. Scaffold → Copy template, configure package.json
38-
4. Implement → Build transformer with TDD
39-
5. Test → Verify against example transformations
40-
6. Document → Write README
38+
4. Convention → Add walkerOS.json metadata and buildDev
39+
5. Implement → Build transformer with TDD
40+
6. Test → Verify against example transformations
41+
7. Document → Write README
4142
```
4243

4344
## Supporting Files
@@ -162,16 +163,54 @@ packages/transformers/[name]/
162163

163164
---
164165

165-
## Phase 4: Implement
166+
## Phase 4: walkerOS.json Convention
167+
168+
Every walkerOS package ships a `walkerOS.json` file for CDN-based schema
169+
discovery.
170+
171+
### Add `walkerOS` field to package.json
172+
173+
```json
174+
{
175+
"walkerOS": {
176+
"type": "transformer"
177+
},
178+
"keywords": ["walkerOS", "walkerOS-transformer", ...]
179+
}
180+
```
181+
182+
### Use `buildDev()` in tsup.config.ts
183+
184+
Replace `buildModules({ entry: ['src/dev.ts'] })` with `buildDev()`:
185+
186+
```typescript
187+
import { buildDev } from '@walkeros/config/tsup';
188+
// In defineConfig array:
189+
buildDev(),
190+
```
191+
192+
This auto-generates `dist/dev/walkerOS.json` from your Zod schemas at build
193+
time.
194+
195+
### Gate: Convention Met
196+
197+
- [ ] `walkerOS` field in package.json with `type: "transformer"`
198+
- [ ] `buildDev()` in tsup.config.ts
199+
- [ ] Build generates `dist/dev/walkerOS.json`
200+
- [ ] Keywords include `walkerOS` and `walkerOS-transformer`
201+
202+
---
203+
204+
## Phase 5: Implement
166205

167206
**Now write code to transform examples as expected.**
168207

169-
### 4.1 Define Types
208+
### 5.1 Define Types
170209

171210
See [templates/validation/types.ts](templates/validation/types.ts) for the
172211
pattern. Define `Settings` and `Types` interfaces.
173212

174-
### 4.2 Implement Transformer (Context Pattern)
213+
### 5.2 Implement Transformer (Context Pattern)
175214

176215
Transformers use the **context pattern** - they receive a single `context`
177216
object containing `config`, `env`, `logger`, `id`, and `collector`.
@@ -188,7 +227,7 @@ complete implementation example.
188227
3. **Push receives pushContext**: The `push` function gets event + push context
189228
4. **Return values**: `event` (continue), `void` (passthrough), `false` (cancel)
190229

191-
### 4.3 Export
230+
### 5.3 Export
192231

193232
`src/index.ts`:
194233

@@ -204,7 +243,7 @@ export type { Settings, Types } from './types';
204243

205244
---
206245

207-
## Phase 5: Test Against Examples
246+
## Phase 6: Test Against Examples
208247

209248
**Verify implementation produces expected outputs.**
210249

@@ -223,7 +262,7 @@ a complete test suite showing:
223262

224263
---
225264

226-
## Phase 6: Document
265+
## Phase 7: Document
227266

228267
Follow the [writing-documentation](../walkeros-writing-documentation/SKILL.md)
229268
skill for:
@@ -251,6 +290,8 @@ requirements (build, test, lint, no `any`):
251290
- [ ] Examples include before/after event pairs
252291
- [ ] Return values handle all cases (event, void, false)
253292
- [ ] Tests use examples for assertions (not hardcoded values)
293+
- [ ] `walkerOS.json` generated at build time
294+
- [ ] `walkerOS` field in package.json
254295

255296
---
256297

skills/walkeros-using-cli/SKILL.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,20 @@ Exit codes:
217217
3 = Input error
218218
```
219219

220+
### Validate a specific entry
221+
222+
Validate a destination, source, or transformer's settings against its package
223+
schema:
224+
225+
```bash
226+
walkeros validate destinations.snowplow
227+
walkeros validate sources.browser
228+
walkeros validate transformers.validator
229+
```
230+
231+
Uses dot-notation: `{section}.{key}`. Fetches the package's `walkerOS.json` from
232+
CDN and validates `config.settings` against the published schema.
233+
220234
### Run Command
221235

222236
```bash

skills/walkeros-writing-documentation/SKILL.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ export * as examples from './examples';
165165
- [ ] Follows package/skill/website templates
166166
- [ ] Terminology matches: walkerOS, collector, destination, source
167167
- [ ] Headings use sentence case (e.g., "Next steps" not "Next Steps")
168+
- [ ] `walkerOS.json` convention followed (walkerOS field in package.json,
169+
buildDev in tsup)
168170

169171
---
170172

@@ -238,6 +240,23 @@ See [src/types.ts](./src/types.ts) for TypeScript interfaces.
238240

239241
````
240242
243+
### walkerOS.json
244+
245+
Every package should document its `walkerOS.json` convention in the README:
246+
247+
```json
248+
{
249+
"walkerOS": {
250+
"type": "destination",
251+
"platform": "web",
252+
"schema": "./dist/dev/walkerOS.json"
253+
}
254+
}
255+
```
256+
257+
Adjust `type` (`destination`, `source`, `transformer`) and `platform` (`web`,
258+
`server`) to match the package.
259+
241260
### Website Doc Template (MDX)
242261
243262
```mdx

0 commit comments

Comments
 (0)