Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Compute Module TypeScript SDK

@palantir/compute-module은 TypeScript와 JavaScript에서 작업을 받아 함수를 실행하고 결과를 전송합니다. ComputeModule, SlsLogger, TypeBox 기반 함수 명세, Pipeline 자원 별칭 및 소스 자격 증명을 제공합니다.

npm install @palantir/compute-module

Yarn 프로젝트에서는 yarn add @palantir/compute-module을 사용합니다. 아래 빌드 예는 SDK 0.2.14와 TypeBox 0.32 계열을 사용합니다.

JavaScript 함수

const { ComputeModule } = require("@palantir/compute-module");

new ComputeModule({})
  .register("addOne", async ({ value }) => ({ value: value + 1 }))
  .register("stringify", async ({ n }) => String(n))
  .default(async () => {
    throw new Error("Unsupported query name");
  });

생성자에 옵션 객체를 전달합니다. 생성자가 작업 수신을 초기화하므로 별도의 start 메서드는 호출하지 않습니다. NODE_ENV가 development이면 SDK가 실제 모듈 수신을 시작하지 않으므로 실행 이미지에는 해당 값을 설정하지 않습니다.

register()의 콜백은 입력 data, context 순서입니다. context의 공개 필드는 jobId입니다. Python과 Java의 context-first 서명과 다릅니다. 각 쿼리에는 하나의 응답 listener를 등록합니다. 알 수 없는 쿼리는 default listener에서 처리할 수 있습니다.

위 예제처럼 definitions가 없으면 함수 명세 자동 등록과 그 명세에 따른 타입 추론이 없습니다. Foundry에서 사용할 입력과 반환 타입을 따로 등록해야 합니다.

TypeBox 명세

import { ComputeModule, SlsLogger } from "@palantir/compute-module";
import { Type } from "@sinclair/typebox";

const logger = new SlsLogger();
const computeModule = new ComputeModule({
  logger,
  definitions: {
    addOne: {
      input: Type.Object({ value: Type.Number() }),
      output: Type.Object({ value: Type.Number() }),
    },
  },
});

computeModule.register("addOne", async ({ value }, context) => {
  logger.info("Processing addOne", { job_id: context.jobId });
  return { value: value + 1 };
});

computeModule.on("responsive", () => logger.info("Compute Module is responsive"));

입력은 Type.Object로 정의하며 출력과 함께 함수 명세로 변환됩니다. TypeScript는 listener의 입력과 반환 타입을 이 정의에서 추론합니다. 모듈이 responsive가 되면 명세를 게시하며 Functions에서 가져와 플랫폼 함수로 등록할 수 있습니다.

isAutoRegistered의 기본은 true입니다. false로 설정하면 타입 추론을 유지하면서 명세 자동 게시를 끕니다. TypeBox 정의는 타입 및 명세 생성에 사용됩니다. 직접 전달한 임의 JSON까지 listener 안에서 자동 검증해 주는 것으로 간주하지 말고 함수의 입력 계약을 유지합니다.

스트리밍

출력 타입은 Type.Array로 선언합니다. 각 write 호출은 JSON 값 하나를 쓰며 마지막에 end를 호출합니다.

import { ComputeModule } from "@palantir/compute-module";
import { Type } from "@sinclair/typebox";

const computeModule = new ComputeModule({
  definitions: {
    greet: {
      input: Type.Object({ name: Type.String() }),
      output: Type.Array(Type.String()),
    },
  },
});

computeModule.registerStreaming("greet", ({ name }, writable, context) => {
  writable.write(JSON.stringify("Hello, "));
  writable.write(JSON.stringify(name));
  writable.end();
});

registerStreaming()의 인자 순서는 data, writable, context입니다. 구조체 배열도 원소별로 전송할 수 있습니다.

import { ComputeModule } from "@palantir/compute-module";
import { Type } from "@sinclair/typebox";

const User = Type.Object({
  name: Type.String(),
  role: Type.String(),
  active: Type.Boolean(),
});
const computeModule = new ComputeModule({
  definitions: {
    activeUsers: {
      input: Type.Object({ users: Type.Array(User) }),
      output: Type.Array(User),
    },
  },
});

computeModule.registerStreaming("activeUsers", ({ users }, writable) => {
  for (const user of users) {
    if (user.active) writable.write(JSON.stringify(user));
  }
  writable.end();
});

이 함수는 입력에 들어온 active 사용자를 전송하며 온톨로지 사용자 목록을 별도로 조회하지 않습니다.

SLS 로그

SlsLogger는 debug, info, warn, error를 제공합니다. 두 번째 인자의 키/값을 로그 매개변수에 넣을 수 있습니다. 토큰과 비밀 및 민감한 입력은 기록하지 않습니다.

logger 옵션에는 console처럼 log, info, warn과 error 메서드를 가진 로거도 전달할 수 있습니다. logger를 생략하면 모듈의 SDK 로그를 출력하지 않습니다. 로그 수집과 상태 확인은 복제본 진단을 따릅니다.

Pipeline 별칭과 실행 환경

import { ComputeModule } from "@palantir/compute-module";

const environment = ComputeModule.getEnvironment();
if (environment.type !== "pipelines") {
  throw new Error("Pipeline execution mode is required");
}
const input = ComputeModule.getResource("inputDataset");
if (input === null) {
  throw new Error("The inputDataset alias is not configured");
}
const datasetRid = input.rid;
const branch = input.branch ?? "master";
const buildToken = environment.buildToken;

inputDataset은 Configure에서 붙인 별칭입니다. getResource()는 RID 문자열이 아니라 rid와 선택 branch를 가진 자원 객체를 반환하며 별칭이 없으면 null입니다. RESOURCE_ALIAS_MAP 환경 구성이 없으면 조회 자체가 실패합니다.

Pipeline 환경의 buildToken은 선언한 입력/출력 자원에 사용하는 토큰입니다. Functions 환경은 type === "functions"로 구분하고 선택 항목 thirdPartyApplication에 clientId와 clientSecret이 제공됩니다. No platform permissions라면 애플리케이션 자격 증명이 없을 수 있습니다. 이 값들을 함수 결과나 로그로 반환하지 않습니다.

소스 검증과 비밀 조회

import { ComputeModule } from "@palantir/compute-module";

const computeModule = new ComputeModule({
  sources: {
    MyApi: { credentials: ["MyCredential"] },
    AnotherApi: {},
  },
});
const credential = computeModule.getCredential("MyApi", "MyCredential");
if (credential === null) {
  throw new Error("MyCredential is not available");
}

MyApi, AnotherApi와 MyCredential은 가상 구성 이름이며 Data Connection에 지정한 실제 API 이름과 비밀 이름에 맞춥니다. sources를 선언하면 시작 시 소스와 명시한 자격 증명의 존재를 검사하며 TypeScript에서도 이름을 제한합니다.

AnotherApi처럼 credentials를 지정하지 않으면 소스의 존재만 검사하며 getCredential("AnotherApi", "AnyString")처럼 임의의 자격 증명 이름을 인자로 사용할 수 있습니다. 실제 비밀이 없으면 null입니다. sources를 생략하면 이 시작 검증을 하지 않으며 마운트 자체가 없으면 자격 증명 조회가 실패할 수 있습니다.

Foundry 내부 서비스 주소

import { ComputeModule, FoundryService } from "@palantir/compute-module";

const streamProxyApi = ComputeModule.getServiceApi(FoundryService.STREAM_PROXY);
if (streamProxyApi === undefined) {
  throw new Error("Stream proxy service is not available");
}

서비스 검색으로 받은 내부 API 주소는 플랫폼 ingress로 되돌아가는 별도의 소스 없이 사용할 수 있습니다. 해당 API의 인증과 자원 권한은 여전히 필요합니다. 공개 Foundry 호스트로 보내는 일반 HTTPS 요청의 소스 네트워크 정책과 구분합니다.

getServiceApi, getResource와 getEnvironment는 정적 메서드를 사용합니다. 이전 인스턴스 메서드 및 environment getter 대신 같은 클래스의 정적 메서드로 옮깁니다.

TypeScript 이미지 빌드 구성

가상 프로젝트의 src/index.ts에는 위 TypeBox addOne 예제를 넣습니다. package.json은 다음과 같이 작성합니다.

{
  "name": "compute-add-one-example",
  "version": "0.0.1",
  "private": true,
  "type": "commonjs",
  "scripts": {"build": "tsc", "start": "node dist/index.js"},
  "dependencies": {
    "@palantir/compute-module": "0.2.14",
    "@sinclair/typebox": "^0.32.35"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "typescript": "^5.7.3"
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "rootDir": "src",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*.ts"]
}

먼저 npm install로 package-lock.json을 만들고 저장합니다. Dockerfile은 TypeScript를 빌드한 뒤 생성한 JavaScript와 런타임 의존성만 복사합니다.

FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run build

FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER 5000
CMD ["node", "dist/index.js"]

Compute Module용 이미지는 linux/amd64로 빌드하고 latest가 아닌 태그 또는 digest를 사용합니다. JavaScript로 직접 작성한다면 빌드 단계 없이 런타임 의존성과 실제 index.js를 복사하는 구성을 사용할 수 있습니다.