Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/nself-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ jobs:
run: |
pnpm install --frozen-lockfile --dir .workers/plugins-registry
pnpm install --frozen-lockfile --dir free/feature-flags/sdk-ts
pnpm install --frozen-lockfile --dir shared
pnpm install --frozen-lockfile --dir free/file-processing/ts
pnpm install --frozen-lockfile --dir free/media-processing/ts

# free/file-processing/ts and free/media-processing/ts both depend on
# @nself/plugin-utils via a "file:../../../shared" path dependency.
# pnpm links the directory but never runs its build script, so
# shared/dist (what "@nself/plugin-utils"'s main/types fields point
# at) does not exist yet — build it before the two plugins are
# typechecked/built against it.
- name: Build shared plugin-utils package
run: pnpm --dir shared run build

- name: Run nself ci gate
env:
Expand Down
24 changes: 24 additions & 0 deletions free/feature-flags/sdk-ts/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Jest config for @nself/feature-flags-client.
*
* The package has no jest.config previously — jest ran with zero
* TypeScript transform and every test failed at the `import` statement.
* ts-jest was already an installed devDependency but never wired up.
*
* Source uses NodeNext module resolution and imports its own sibling
* modules with an explicit `.js` extension (e.g. `./index.js`) even though
* the file on disk is `index.ts` — the standard NodeNext-ESM-style
* convention. Jest's CommonJS resolver does not do that rewrite on its
* own, so moduleNameMapper strips the `.js` suffix back off before
* resolution, letting ts-jest's transform pick up the matching `.ts`
* file.
*/

/** @type {import('jest').Config} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
};
1 change: 1 addition & 0 deletions free/feature-flags/sdk-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"devDependencies": {
"@types/jest": "^29.0.0",
"@types/node": "^20.10.0",
"@types/react": "^19.0.0",
"jest": "^29.0.0",
"ts-jest": "^29.0.0",
Expand Down
69 changes: 36 additions & 33 deletions free/feature-flags/sdk-ts/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion free/feature-flags/sdk-ts/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
"skipLibCheck": true,
"isolatedModules": true
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts", "dist"]
Expand Down
2 changes: 2 additions & 0 deletions shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from './http.js';
export * from './validation.js';
export * from './security.js';
export * from './app-context.js';
export * from './metrics.js';

// Re-export commonly used items at top level
export { createLogger, Logger } from './logger.js';
Expand Down Expand Up @@ -51,3 +52,4 @@ export {
parseCsvList,
buildAccountConfigs,
} from './app-context.js';
export { createMetrics, type PluginMetrics } from './metrics.js';
46 changes: 46 additions & 0 deletions shared/src/metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Minimal Prometheus-text-format request/error counters for nself plugins.
*
* createMetrics(name) returns a per-plugin counter pair exposed at a
* plugin's `/metrics` route (see free/file-processing/ts/src/server.ts and
* free/media-processing/ts/src/server.ts, which both hook Fastify's
* onRequest/onError and serve `metrics.format()` as `text/plain;
* version=0.0.4`). Deliberately dependency-free (no prom-client) since a
* plugin only needs two monotonic counters, not full histogram/summary
* support.
*/

export interface PluginMetrics {
/** Increment the request counter. Call once per inbound request. */
incrementRequest: () => void;
/** Increment the error counter. Call once per request that errors. */
incrementError: () => void;
/** Render both counters in Prometheus text exposition format. */
format: () => string;
}

export function createMetrics(name: string): PluginMetrics {
const metricName = name.replace(/[^a-zA-Z0-9_]/g, '_');
let requestCount = 0;
let errorCount = 0;

return {
incrementRequest(): void {
requestCount += 1;
},
incrementError(): void {
errorCount += 1;
},
format(): string {
return [
`# HELP ${metricName}_requests_total Total number of requests handled.`,
`# TYPE ${metricName}_requests_total counter`,
`${metricName}_requests_total ${requestCount}`,
`# HELP ${metricName}_errors_total Total number of requests that errored.`,
`# TYPE ${metricName}_errors_total counter`,
`${metricName}_errors_total ${errorCount}`,
'',
].join('\n');
},
};
}