계약

contract 모듈은 원격 API, 워크플로우, 함수를 위한 타입화된 서비스 바인딩을 엽니다. 계약은 스키마 검증, 비동기 호출, 호출 컨텍스트 전파를 지원합니다. 이 페이지는 API 참조이며, current_user 같은 ID와 값은 애플리케이션 소유 엔트리 및 주변 핸들러 상태를 나타냅니다.

로딩

local contract = require("contract")

바인딩 열기

레지스트리 ID로 바인딩을 엽니다.

local greeter, err = contract.open("app.services:greeter")
if err then
    return nil, err
end

local result, err = greeter:say_hello("Alice")
if err then
    return nil, err
end

바인딩에는 스코프 값, 쿼리 파라미터 또는 호출 옵션도 전달할 수 있습니다.

-- With scope table
local svc, err = contract.open("app.services:user", {
    tenant_id = "acme",
    region = "us-east"
})

-- With query parameters (auto-converted: "true"→bool, numbers→int/float)
local api, err = contract.open("app.services:api?debug=true&timeout=5000")

-- With call options (third argument)
local inst, err = contract.open("app.services:flaky", nil, {
    retry = { max_attempts = 5, initial_delay = 100 }
})
파라미터 타입 설명
binding_id string 바인딩 ID, 쿼리 파라미터 지원
scope table 컨텍스트 값 (선택적, 쿼리 파라미터 재정의)
options table 호출 옵션 (선택적) — 예: retry.max_attempts, retry.initial_delay

반환: Instance, error

계약 가져오기

인트로스펙션을 위해 계약 정의를 검색합니다:

local c, err = contract.get("app.services:greeter")
if err then
    return nil, err
end

print(c:id())  -- "app.services:greeter"

local methods = c:methods()
for _, m in ipairs(methods) do
    print(m.name, m.description)
end

local method, err = c:method("say_hello")
if err then
    return nil, err
end

메서드 정의

필드 타입 설명
name string 메서드 이름
description string 메서드 설명
input_schemas table[] 입력 스키마 정의 (메서드가 선언하지 않으면 없음)
output_schemas table[] 출력 스키마 정의 (메서드가 선언하지 않으면 없음)

구현 찾기

계약을 구현하는 모든 바인딩을 나열합니다:

local bindings, err = contract.find_implementations("app.services:greeter")
if err then
    return nil, err
end

for _, binding_id in ipairs(bindings) do
    print(binding_id)
end

또는 계약 객체를 통해:

local c, err = contract.get("app.services:greeter")
if err then
    return nil, err
end
local bindings, err = c:implementations()
if err then
    return nil, err
end

구현 확인

인스턴스가 계약을 구현하는지 확인합니다:

if contract.is(instance, "app.services:greeter") then
    instance:say_hello("World")
end

메서드 호출

동기 호출 - 완료까지 블록:

local calc, err = contract.open("app.services:calculator")
if err then
    return nil, err
end

local sum, err = calc:add(10, 20)
if err then
    return nil, err
end
local product, err = calc:multiply(5, 6)
if err then
    return nil, err
end

비동기 호출

비동기 실행을 위해 _async 접미사 추가:

local processor, err = contract.open("app.services:processor")
if err then
    return nil, err
end

local future, err = processor:process_async(large_dataset)
if err then
    return nil, err
end

-- Do other work...

-- Wait for result
local ch = future:response()
local _, open = ch:receive()
if not open then
    return nil, errors.new("future response channel closed")
end

local payload, result_err = future:result()
if result_err then return nil, result_err end
local result, data_err = payload:data()
if data_err then return nil, data_err end

Future 메서드는 Futures를 참조하세요.

계약을 통해 열기

계약 객체를 통해 바인딩을 엽니다. 아래 호출은 대안입니다. 인스턴스를 사용하기 전에 contract.get()과 선택한 open() 호출에서 반환된 오류를 확인하세요.

local c, err = contract.get("app.services:user")
if err then
    return nil, err
end

-- Default binding
local instance, err = c:open()

-- Specific binding
local instance, err = c:open("app.services:user_impl")

-- With scope
local instance, err = c:open(nil, {user_id = 123})
local instance, err = c:open("app.services:user_impl", {user_id = 123})

컨텍스트 추가

미리 구성된 컨텍스트로 래퍼를 생성합니다:

local ctx = require("ctx")
local c, err = contract.get("app.services:user")
if err then return nil, err end

local request_id, ctx_err = ctx.get("request_id")
if ctx_err then return nil, ctx_err end

local wrapped, err = c:with_context({
    request_id = request_id,
    user_id = current_user.id
})
if err then return nil, err end

local instance, err = wrapped:open()

호출 옵션

with_options를 통해 재시도 및 기타 호출 동작을 구성합니다:

local c, err = contract.get("app.services:flaky")
if err then return nil, err end

local configured = c:with_options({
    retry = { max_attempts = 5, initial_delay = 100 }
})
local inst, err = configured:open("app.services:flaky_impl")
if err then return nil, err end

local result, err = inst:call()

옵션은 반환된 인스턴스의 모든 메서드 호출에 적용됩니다. 재시도 가능한 오류만 재시도를 트리거하며, 재시도 불가능한 오류는 즉시 반환됩니다. with_options는 with_context, with_actor, with_scope와 체이닝할 수 있습니다.

옵션 타입 설명
retry.max_attempts int 첫 번째를 포함한 최대 시도 횟수 (1은 재시도 비활성화)
retry.initial_delay int/duration 첫 번째 재시도 전 지연 (ms 또는 duration 문자열), 기본값 100
retry.max_delay int/duration 백오프 지연의 상한 (ms 또는 duration 문자열), 기본값 10s
retry.backoff_factor number 시도할 때마다 지연에 적용되는 배수, 기본값 2.0
retry.jitter number 각 지연에 적용되는 무작위 지터 비율, 기본값 0.1
retry.retry_kinds string[] 이 종류의 에러만 재시도; 기본적으로 Invalid, PermissionDenied, Internal을 제외한 모든 종류를 재시도
retry.skip_kinds string[] 이 종류의 에러는 재시도하지 않음

보안 컨텍스트

인가를 위해 액터와 스코프를 설정합니다:

local security = require("security")
local c, err = contract.get("app.services:admin")
if err then return nil, err end

local secured, err = c:with_actor(security.actor())
if err then return nil, err end

secured, err = secured:with_scope(security.scope())
if err then return nil, err end

local admin, err = secured:open()
if err then return nil, err end

명시적인 with_actor/with_scope 없이 열린 계약은 호출자의 앰비언트 액터와 스코프를 상속합니다. 설정된 경우 바인딩된 구현 함수로 전파됩니다 — 인스턴스의 모든 메서드 호출이 해당 신원 아래에서 실행됩니다.

권한

권한 리소스 함수
contract.get 계약 id get()
contract.open 바인딩 id open(), Contract:open()
contract.implementations 계약 id find_implementations(), Contract:implementations()
contract.call 메서드 이름 동기 및 비동기 메서드 호출
contract.context "context" Contract:with_context()
contract.security "security" Contract:with_actor(), Contract:with_scope()

에러

조건 종류
잘못된 바인딩 ID 형식 errors.INVALID
계약을 찾을 수 없음 errors.NOT_FOUND
바인딩을 찾을 수 없음 errors.NOT_FOUND
메서드를 찾을 수 없음 errors.NOT_FOUND
기본 바인딩 없음 errors.NOT_FOUND
권한 거부됨 errors.PERMISSION_DENIED
호출 실패 구현체 에러의 종류가 보존됨; 디스패치 실패는 errors.INTERNAL