diff --git a/.agents/skills/apm-integrations/SKILL.md b/.agents/skills/apm-integrations/SKILL.md index 59399cffeb0..1f45ec5e13e 100644 --- a/.agents/skills/apm-integrations/SKILL.md +++ b/.agents/skills/apm-integrations/SKILL.md @@ -165,7 +165,8 @@ Follow these steps when creating or modifying an integration: 1. **Investigate** — Read the upstream library's source (see [Read Upstream Source First](#read-upstream-source-first)). Read 1-2 reference integrations of the same type (see table above). Understand the instrumentation and plugin patterns before writing code. 2. **Implement instrumentation** — Create the instrumentation in `packages/datadog-instrumentations/src/`. Use orchestrion for instrumentation. 3. **Implement plugin** — Create the plugin in `packages/datadog-plugin-/src/`. Extend the correct base class. -4. **Register** — Add entries in `packages/dd-trace/src/plugins/index.js`, `index.d.ts`, `docs/test.ts`, `docs/API.md`, and `.github/workflows/apm-integrations.yml`. +4. **Register** — Add entries in `packages/dd-trace/src/plugins/index.js`, every supported public TypeScript surface, + `docs/test.ts`, `docs/API.md`, and `.github/workflows/apm-integrations.yml`. 5. **Write tests** — Add unit tests and ESM integration tests. See [Testing](references/testing.md) for templates. 6. **Run tests** — Validate with: @@ -176,7 +177,7 @@ Follow these steps when creating or modifying an integration: # If the plugin needs external services (databases, message brokers, etc.), # check docker-compose.yml for available service names, then: docker compose up -d - PLUGINS="" npm run test:plugins:ci + SERVICES="" PLUGINS="" npm run test:plugins:ci ``` 7. **Verify** — Confirm all tests pass before marking work as complete. diff --git a/.agents/skills/apm-integrations/references/new-integration-guide.md b/.agents/skills/apm-integrations/references/new-integration-guide.md index 30fcefb8e34..b93e868fad3 100644 --- a/.agents/skills/apm-integrations/references/new-integration-guide.md +++ b/.agents/skills/apm-integrations/references/new-integration-guide.md @@ -177,6 +177,7 @@ class MyPlugin extends DatabasePlugin { // Orchestrion: static prefix = 'tracing:orchestrion::' // Shimmer + tracingChannel: static prefix = 'tracing:apm::' // Shimmer + manual channels: omit prefix — defaults to `apm:${id}:${operation}` + static prefix = '' static peerServicePrecursors = ['db.name'] bindStart (ctx) { @@ -196,6 +197,11 @@ class MyPlugin extends DatabasePlugin { return ctx.currentStore } + + // Choose `end` (sync), `asyncEnd` (promise/callback), or `finish` (legacy manual channel). + asyncEnd (ctx) { + this.finish(ctx) + } } module.exports = MyPlugin @@ -215,7 +221,7 @@ If multiple npm packages map to the same plugin (e.g., `redis` and `@redis/clien ## Step 4: Add TypeScript Definitions -In `index.d.ts`, add to the `plugins` namespace: +Add the plugin type to the `plugins` namespace in every supported public TypeScript surface: ```typescript // In the Plugins interface: @@ -296,7 +302,7 @@ PLUGINS="" npm run test:plugins:ci - [ ] Registered in hooks.js (required for both orchestrion and shimmer paths) - [ ] Plugin created with correct base class - [ ] Plugin registered in `packages/dd-trace/src/plugins/index.js` -- [ ] TypeScript definitions added to `index.d.ts` +- [ ] TypeScript definitions added to every supported public TypeScript surface - [ ] Type check added to `docs/test.ts` - [ ] Documentation added to `docs/API.md` - [ ] CI job added to `.github/workflows/apm-integrations.yml` diff --git a/.agents/skills/apm-integrations/references/plugin-patterns.md b/.agents/skills/apm-integrations/references/plugin-patterns.md index 8a14a796503..047175c7afc 100644 --- a/.agents/skills/apm-integrations/references/plugin-patterns.md +++ b/.agents/skills/apm-integrations/references/plugin-patterns.md @@ -14,7 +14,7 @@ The channel prefix is determined by the instrumentation type. Node.js `tracingCh When using shimmer, prefer `tracingChannel` over manual channels — it provides `start/end/asyncStart/asyncEnd/error` events automatically, consistent with how orchestrion works internally. -This means the plugin only needs to define static properties and implement `bindStart`: +This asynchronous Orchestrion example creates the span in `bindStart` and finishes it in `asyncEnd`: ### Orchestrion Plugin (preferred) ```javascript @@ -29,6 +29,10 @@ class MyPlugin extends TracingPlugin { }, ctx) return ctx.currentStore } + + asyncEnd (ctx) { + this.finish(ctx) + } } ``` diff --git a/.agents/skills/apm-integrations/references/testing.md b/.agents/skills/apm-integrations/references/testing.md index d500d0d52a0..8f4ca3ac06c 100644 --- a/.agents/skills/apm-integrations/references/testing.md +++ b/.agents/skills/apm-integrations/references/testing.md @@ -144,7 +144,6 @@ const { withVersions } = require('../../../dd-trace/test/setup/mocha') describe('esm', () => { let agent let proc - let variants withVersions('', '', version => { useSandbox([`'@${version}'`], false, [ @@ -154,8 +153,12 @@ describe('esm', () => { agent = await new FakeAgent().start() }) - before(async function () { - variants = varySandbox('server.mjs', '', '') + const variants = varySandbox('server.mjs', { + bindingName: 'myLib', + packageName: '', + defaultExport: true, + namedExports: [''], + namedExportBinding: 'namespace', }) afterEach(async () => { @@ -163,7 +166,7 @@ describe('esm', () => { await agent.stop() }) - for (const variant of varySandbox.VARIANTS) { + for (const variant of Object.keys(variants)) { it(`is instrumented ${variant}`, async () => { const res = agent.assertMessageReceived(({ headers, payload }) => { assert.strictEqual(headers.host, `127.0.0.1:${agent.port}`) @@ -182,9 +185,9 @@ describe('esm', () => { ### Key ESM Test Concepts -- `varySandbox(filename, bindingName, namedExport, packageName, byPassDefault)` generates three import-style variants (default, star, destructure) to verify all ESM import patterns -- `varySandbox.VARIANTS` is `['default', 'star', 'destructure']` -- Pass `byPassDefault: true` as fifth argument when the module has no default export +- `varySandbox(filename, options)` generates the import variants supported by the package's export shape. +- Set `defaultExport`, `namedExports`, and `namedExportBinding` from the installed package's real exports. +- Iterate over `Object.keys(variants)`; the returned object maps each generated variant to its filename. - `useSandbox` installs package versions into a temp sandbox directory - `spawnPluginIntegrationTestProcAndExpectExit` spawns `node