npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ingentic/serverless-function-wrapper

v0.3.0

Published

Public wrapper for ingentic serverless functions: HTTP shell, S3/R2 storage, file pipeline, registry export, health and metrics.

Downloads

616

Readme

@ingentic/serverless-function-wrapper

Public wrapper for ingentic serverless functions. It provides the platform boilerplate once — HTTP shell, S3/R2 storage, the file pipeline, registry export, health and metrics — so that each business function ships only its business logic plus a small functionDefinition.

The wrapper has zero knowledge of any business domain (drugs, RxNorm, OWL, templates, ...). It deals only in generic concepts: HTTP, object storage, file references and constants, envelopes, and the function registry.

How a business function uses the wrapper

Each function is a separate, independently deployed container that depends on this package. Its entry point is a few lines:

import { startServer } from "@ingentic/serverless-function-wrapper";
import definition from "./function.js";

startServer(definition);

The automation runtime continues to call each function's POST /function directly; it never sees the wrapper.

The functionDefinition contract

{
  functionId: "fn-...",              // registry identity (env override: FUNCTION_ID)
  functionName: "SomeFunction",      // env override: FUNCTION_NAME
  functionPurpose: "One sentence.",  // env override: FUNCTION_PURPOSE
  serviceName: "some-function",      // logging (default: SERVICE_NAME env, else functionId)
  defaultPort: 8080,                 // optional
  bodyLimit: "1mb",                  // optional
  inputParameters: [{ name, type: "string"|"array", required: true }], // optional; defaults derived from inputs

  inputs: {
    "<bodyParam>": {
      kind: "file" | "value" | "file-insert", // file: S3 key -> fetched; value: constant/literal; file-insert: array of key+literal objects -> fetched content
      parse: "text" | "json",        // how to parse the file content / literal (text for file-insert)
      multiple: true,                // optional: accept a string or an array of strings (file/value only)
      fileKeyField: "<keyProp>",     // file-insert only: property holding the S3 object key
      literalFields: ["<litProp>"],  // file-insert only: other required string properties, passed through
      contentField: "<contentProp>", // file-insert only: property the wrapper fills with each file's content
      validate: (value) => boolean,  // optional: extra validation; failure -> 400
      required: true,                // optional, default true
    },
  },

  outputParamName: "outputFileKey",  // response key + registry outputParameter
  outputKey: "outputBase",           // string, or (inputKeys) => base; wrapper appends -<timestamp>.<ext>
  outputExtension: "json",           // optional, default "json"
  outputContentType: "application/json", // optional
  outputRaw: false,                  // optional: write the body as-is (a string) instead of JSON-serializing it

  // Instead of the legacy single-output fields above, a function may declare an
  // `outputs` array for multiple named outputs. When present, `outputs` takes
  // precedence and the legacy fields are ignored. Each entry is:
  //   { name: "ontologyId", kind: "value" }                        // inline scalar in the response
  //   { name: "resultFileKey", kind: "file", key: "resultBase",    // S3-backed; response carries the key
  //       extension: "json", contentType: "application/json", raw: false }
  // `key` mirrors `outputKey` (string or (inputKeys) => base); the registry export
  // marks file outputs with `fileKey: true` so consumers know the value is a key.

  readiness: ["s3Storage"],          // or (ctx) => ({ [name]: "up"|"down" })

  handler: async (inputs, context) => {
    // inputs[param] = fetched/parsed value for each declared input
    // context = { requestId, logger, storage, bucket, buildOutputKey }
    // Single-output: returns { body: <object to serialize to S3>, info?: <stats> }
    // Multi-output: returns { body: { <outputName>: <data|scalar>, ... }, info? }
    //   - `value` output -> body[name] is a non-empty string (returned inline)
    //   - `file` output   -> body[name] is the data to persist (JSON-serialized, or a string when `raw`)
    // throw BadRequestError / HttpError for 4xx; anything else -> masked 500
  },
}

Input kinds

  • file — the request parameter is an object key (a reference to a file, not the file). The wrapper validates the key, fetches the object from the configured bucket, and parses it (text or json).

  • value — the request parameter is a string constant or literal (including JSON strings) passed through from a calling function. The wrapper validates it is a string and optionally JSON-parses it.

  • file-insert — the request parameter is a non-empty array of objects, each pairing a file key (a reference to a file in the configured bucket) with one or more literal values. The wrapper validates each key and each declared literal field, fetches each referenced file, and delivers each object enriched with the fetched content under the declared contentField. fileKeyField, literalFields, and contentField are all required and named by the function:

    insertions: {
      kind: "file-insert",
      fileKeyField: "sourceFileKey",      // property holding the S3 object key
      literalFields: ["insertionPoint"],  // user-supplied literal properties
      contentField: "sourceContent",      // wrapper fills this with each file's content
    },

    For a request body { insertions: [{ sourceFileKey: "a.owl", insertionPoint: "<!-- Classes -->" }] }, the handler receives [{ sourceFileKey: "a.owl", insertionPoint: "<!-- Classes -->", sourceContent: "<fetched content>" }] in array order. The registry export marks the file-key property with fileKey: true and the wrapper-populated content field with internal: true (a field the runtime passes through without marshalling). Content is fetched as text by default; set parse: "json" to have each file JSON-parsed before it is attached.

Either kind can be a single value or, with multiple: true, an array of values (an array of keys, or an array of constants). The registry inputParameters type reflects this as string or array.

Multiple outputs (outputs)

A function may declare an optional outputs array instead of the legacy single-output fields (outputParamName/outputKey/outputExtension/outputContentType/outputRaw). When present, the legacy fields are ignored. This lets a function return small scalar values inline (e.g. an ontologyId) alongside an S3-backed output, keeping response bodies small regardless of artifact size:

outputs: [
  { name: "ontologyId", kind: "value" },
  {
    name: "ontologyUploadFileKey",
    kind: "file",
    key: "ontologyUpload",
    extension: "json",
    contentType: "application/json",
  },
],
  • name (required) — the response key and registry outputParameter name; must be unique and must not collide with an input name.
  • kind (required) — "value" (inline scalar) or "file" (S3-backed).
  • key (file only) — base S3 key: a string, or (inputKeys) => base; the wrapper appends -<timestamp>.<ext>.
  • extension (file only) — default "json".
  • contentType (file only) — optional; emitted in the registry export when set.
  • raw (file only) — default false. When true, the value must be a string and is written verbatim; the registry export emits raw: true.

The handler returns { body, info } where body carries one key per declared output:

  • kind: "file"body[name] is the data to persist (JSON-serialized, or a raw string when raw: true).
  • kind: "value"body[name] is a non-empty string returned inline in the response.

A declared output missing from body, a non-string/empty value output, or a non-string file value when raw is enabled are handler contract violations and fail the request with a masked 500. info remains logging-only.

The registry export emits one outputParameter per entry: value{ name, type: "string", required: true }; file{ name, type: "string", required: true, fileKey: true, ... } so consuming services know the value is a key to fetch.

The pipeline

POST /function (POST-only; query parameters rejected) → validate each declared input → fetch file inputs / pass through value inputs → call the business handler → for each declared output (multi-output) or the single output (legacy): write file outputs to S3 under {key}-<timestamp>.<ext> and place value outputs inline → respond with the assembled object ({ [outputName]: key | value, ... }).

When outputs is absent, the legacy single-output pipeline runs unchanged: write the returned body (JSON-serialized by default, or raw when outputRaw is set) under {outputKey}-<timestamp>.<ext> and respond { [outputParamName]: key }.

Public API

| Export | Purpose | | ------ | ------- | | startServer(definition) | Boot the HTTP server (PORT, graceful shutdown, definition validation). | | createApp(definition) | Return an Express app (for tests / embedding). | | runFunction(definition, requestData) | Run the pipeline for a single request. | | buildRegistryConfig(definition) | Build the GET /function/json payload. | | buildOutputKey(base, ext, date?) | Timestamped output key builder. | | buildEnvelope({ info, params }) | Platform { status, info, params } success envelope. | | BadRequestError, HttpError | Error types business handlers throw; the wrapper maps them to status codes. | | S3Service | Storage client (getObject, putObject, getFile, validateKey, isConfigured, getConfiguredBucket). | | validateDefinition(definition) | Startup validation of a definition. |

Endpoints served by every function

  • GET /healthz — liveness.
  • GET /readyz — readiness from the definition's readiness checks.
  • GET /metrics — Prometheus metrics.
  • POST /function — the function.
  • GET /function/json — registry configuration export. Each response.outputParameters entry carries contentType (and raw: true) when the definition declares outputContentType/outputRaw, so consuming services know the output's format; entries from an outputs array carry fileKey: true when the output is S3-backed. file-insert inputs are advertised with kind: "file-insert" and an items schema whose content field is marked internal: true.

Environment variables

| Variable | Required | Default | Description | | -------- | -------- | ------- | ----------- | | PORT | No | definition.defaultPort | 8080 | HTTP port | | ALLOWED_ORIGINS | No | * | Comma-separated CORS origins | | SERVICE_NAME | No | definition.serviceName | functionId | Structured-log service name | | S3_ENDPOINT | Yes | - | S3/R2 API endpoint (e.g. Cloudflare R2 account endpoint) | | S3_ACCESS_KEY | Yes | - | Access key | | S3_SECRET_ACCESS_KEY | Yes | - | Secret access key | | S3_BUCKET_NAME | Yes | - | Bucket for input and output objects | | FUNCTION_ID / FUNCTION_SLUG / FUNCTION_URI / FUNCTION_NAME / FUNCTION_PURPOSE | No | definition defaults | GET /function/json overrides | | FUNCTION_REGISTRY_ID | No | - | Emits registryId in GET /function/json when set |

Configuration is supplied externally (Kubernetes Secrets/ConfigMaps or equivalent). .env files exist only for local development and must never be committed or embedded in images.

Onboarding a new function

Copy templates/function/ to a new function repository, fill in the placeholders (see templates/function/README.md), and add your business logic under src/domain/. The template is a repo-level aid; it is not part of the published package.

Governance

Versioning, release, and pinning policies live in GOVERNANCE.md. Notable rules:

  • The wrapper is consumed by functions as an npm dependency only — no file: or other local-path inter-project dependencies.
  • Functions depend with a caret range (e.g. ^0.1.2); keep functions within one minor of the latest release and use Renovate/Dependabot per function.

Development

npm install
npm test
npm run lint

License

ISC