Lingara 개발자 문서
이 페이지는 영어에서 번역되었습니다. 두 내용이 다를 경우 영어 페이지가 정확합니다. 영어 페이지 읽기
연결은 단계로 이루어지며, 어떤 단계든 스크립트를 가질 수 있습니다. 단계의 스크립트는 함수의 본문입니다. 스크립트는 두 가지 값을 받습니다. 이 페이지의 API인 lingara, 그리고 단계 자체의 입력 항목에 그 클라이언트와 그 앞에 연결된 단계를 더한 input입니다. input의 타입은 단계의 종류에 따라 달라지므로, 웹훅 단계의 스크립트는 웹훅 단계의 입력 항목을 읽습니다. 스크립트는 아래 헬퍼 중 하나로 만든 객체를 정확히 하나 반환합니다.
스크립트는 시크릿을 절대 보지 못합니다. 단계에 클라이언트가 필요하면 input.client 같은 핸들을 받고, 헬퍼는 그 핸들을 기록합니다. 실제 값은 연결을 적용할 때 Lingara가 채웁니다.
스크립트가 해서는 안 되는 일
스크립트는 여러분의 브라우저에서, 네트워크도 저장소도 없는 워커 안에서 실행됩니다. 다음 제한이 적용됩니다.
- 1초 안에 끝나야 합니다.
- 반환하는 문자열은 최대 2048자입니다.
- 반환하는 목록은 최대 64개 항목입니다.
- 전체 결과는 최대 16 KiB입니다.
제한을 어기거나, 예외를 던지거나, 헬퍼 하나의 결과가 아닌 것을 반환하는 스크립트는 그 단계를 실패하게 하며, “확인하기”가 어느 단계가 왜 실패했는지 보여 줍니다.
버전 약속
이 페이지의 모든 것은 lingara.v1입니다. v1 안에서 API는 늘어나기만 합니다. 새 헬퍼, 새 선택적 옵션, 새 단계 종류가 나타날 수는 있지만, 지금 사용할 수 있는 것이 삭제되거나 이름이 바뀌거나 범위가 좁아지는 일은 없습니다. 스크립트를 깨뜨릴 변경은 이 버전 옆에 새 버전으로 나오며, v1은 그대로 유지됩니다.
선언
아래의 각 선언은 스크립트 편집기의 자동 완성이 참조하는 것과 같은 파일에서 생성됩니다.
NodeHandle
"n1"처럼 다른 단계의 ID입니다. 의존하는 단계는 자신의 클라이언트를 핸들로 지정합니다.
export type NodeHandle = string;
Scope
API 클라이언트가 가질 수 있는 권한입니다. 여러분의 계정이 어떤 권한을 사용할 수 있는지는 서버가 판단합니다.
export type Scope = 'vocab:generate' | 'lesson_plans:read' | 'lesson_plans:write' | 'tutor:converse' | 'usage:read' | 'events:read' | 'events:write' | 'embed:mint' | 'embed:play';
AppSlot
Lingara 앱에서 앱의 카드가 나타날 수 있는 위치입니다.
export type AppSlot = 'plans.empty_detail' | 'home.side';
ContextSlice
앱이 받겠다고 요청할 수 있는 학습자 컨텍스트의 한 부분입니다.
export type ContextSlice = 'languages' | 'plan_summary' | 'review_due' | 'tutor_topic';
ClientOptions
lingara.client의 옵션입니다.
export interface ClientOptions {
name: string;
scopes: readonly Scope[];
redirectUris?: readonly string[];
}
name(string) — 개발자 도구에 표시되는 클라이언트의 이름.scopes(readonly Scope[]) — 클라이언트가 요청하는 권한.redirectUris?(readonly string[]) — 인가 코드 흐름이 학습자를 되돌려 보낼 수 있는 곳. 서버 간 클라이언트에서는 생략합니다.
WebhookOptions
lingara.webhook의 옵션입니다.
export interface WebhookOptions {
client: NodeHandle;
url: string;
events: readonly string[];
}
client(NodeHandle) — 이 웹훅이 속한 클라이언트 단계:input.client.url(string) — Lingara가 이벤트를 전달하는 HTTPS 주소.events(readonly string[]) — 전달할 이벤트 유형(예:"lesson_plan.ready").
ManifestOptions
lingara.manifest의 옵션으로, camelCase로 씁니다. 헬퍼가 매니페스트의 전송 형식을 작성합니다.
export interface ManifestOptions {
name: string | Readonly<Record<string, string>>;
description: string | Readonly<Record<string, string>>;
renderUrl: string;
slots: readonly AppSlot[];
context?: readonly ContextSlice[];
scopes?: readonly Scope[];
tutorNote?: boolean;
defaultLocale?: string;
}
name(string | Readonly<Record<string, string>>) — 앱의 이름: 문자열 하나, 또는 로캘 코드마다 문자열 하나.description(string | Readonly<Record<string, string>>) — 앱에 대한 한 문장: 문자열 하나, 또는 로캘 코드마다 문자열 하나.renderUrl(string) — Lingara가 앱의 카드를 요청하는 HTTPS 주소.slots(readonly AppSlot[]) — 앱의 카드가 나타날 수 있는 위치.context?(readonly ContextSlice[]) — 앱이 받는 컨텍스트 부분. 기본값은 없음.scopes?(readonly Scope[]) — 앱이 학습자에게 요청하는 권한. 기본값은 없음.tutorNote?(boolean) — 앱이 튜터에게 메모를 남길 수 있는지 여부. 기본값은false.defaultLocale?(string) — 일반 문자열로 쓴 이름이나 설명이 저장되는 로캘. 기본값은"en".
AppOptions
lingara.app의 옵션입니다.
export interface AppOptions {
client: NodeHandle;
manifest: Manifest;
}
client(NodeHandle) — 이 앱이 속한 클라이언트 단계:input.client.manifest(Manifest) —lingara.manifest로 만든 앱의 매니페스트.
ClientSpec
클라이언트 단계가 만드는 것: API 클라이언트.
export interface ClientSpec {
readonly kind: 'client';
readonly name: string;
readonly scopes: readonly Scope[];
readonly redirectUris?: readonly string[];
}
kind('client') — 항상"client".name(string) — 클라이언트의 이름.scopes(readonly Scope[]) — 클라이언트가 요청하는 권한.redirectUris?(readonly string[]) — 클라이언트의 리디렉션 URI(있는 경우).
SecretSpec
시크릿 단계가 만드는 것: 해당 클라이언트의 새 시크릿.
export interface SecretSpec {
readonly kind: 'secret';
readonly client: NodeHandle;
}
kind('secret') — 항상"secret".client(NodeHandle) — 시크릿이 속한 클라이언트 단계.
WebhookSpec
웹훅 단계가 만드는 것: 웹훅 엔드포인트.
export interface WebhookSpec {
readonly kind: 'webhook';
readonly client: NodeHandle;
readonly url: string;
readonly events: readonly string[];
}
kind('webhook') — 항상"webhook".client(NodeHandle) — 웹훅이 속한 클라이언트 단계.url(string) — 이벤트가 전달되는 주소.events(readonly string[]) — 전달되는 이벤트 유형.
AppSpec
앱 단계가 만드는 것: Lingara 안에 카드를 그리는 앱.
export interface AppSpec {
readonly kind: 'app';
readonly client: NodeHandle;
readonly manifest: Manifest;
}
kind('app') — 항상"app".client(NodeHandle) — 앱이 속한 클라이언트 단계.manifest(Manifest) — 앱의 매니페스트.
Manifest
서버가 저장하는 그대로의, 전송 형식의 앱 매니페스트입니다.
export interface Manifest {
readonly manifest_version: 1;
readonly default_locale: string;
readonly name: Readonly<Record<string, string>>;
readonly description: Readonly<Record<string, string>>;
readonly render_url: string;
readonly slots: readonly AppSlot[];
readonly context: readonly ContextSlice[];
readonly scopes: readonly Scope[];
readonly tutor_note: boolean;
}
manifest_version(1) — 항상1.default_locale(string) — 학습자의 로캘에 해당 항목이 없을 때 표시되는 로캘.name(Readonly<Record<string, string>>) — 로캘 코드별 앱의 이름.description(Readonly<Record<string, string>>) — 로캘 코드별, 앱에 대한 한 문장.render_url(string) — Lingara가 앱의 카드를 요청하는 HTTPS 주소.slots(readonly AppSlot[]) — 앱의 카드가 나타날 수 있는 위치.context(readonly ContextSlice[]) — 앱이 받는 컨텍스트 부분.scopes(readonly Scope[]) — 앱이 학습자에게 요청하는 권한.tutor_note(boolean) — 앱이 튜터에게 메모를 남길 수 있는지 여부.
Spec
헬퍼가 반환하는 모든 spec입니다. 스크립트는 정확히 하나를 반환합니다.
export type Spec = ClientSpec | SecretSpec | WebhookSpec | AppSpec;
Upstream
바로 앞에 연결된 각 상위 단계의 출력을 핸들별로 모은 것입니다.
export type Upstream = Readonly<Record<NodeHandle, Spec & {
readonly handle: NodeHandle;
}>>;
ClientInput
클라이언트 단계의 입력: 단계의 입력 항목으로, lingara.client의 옵션과 같습니다.
export interface ClientInput {
readonly name: string;
readonly scopes: readonly Scope[];
readonly redirectUris?: readonly string[];
readonly upstream: Upstream;
}
name(string) — 클라이언트의 이름.scopes(readonly Scope[]) — 클라이언트가 요청하는 권한.redirectUris?(readonly string[]) — 클라이언트의 리디렉션 URI(단계에 있는 경우).upstream(Upstream) — 바로 앞 상위 단계 각각의 출력.
SecretInput
시크릿 단계의 입력입니다. client는 이 단계에 연결된 단 하나의 클라이언트 단계입니다.
export interface SecretInput {
readonly client: NodeHandle;
readonly upstream: Upstream;
}
client(NodeHandle) — 이 단계에 연결된 클라이언트 단계.upstream(Upstream) — 바로 앞 상위 단계 각각의 출력.
WebhookInput
웹훅 단계의 입력: 단계의 입력 항목과 연결된 클라이언트.
export interface WebhookInput {
readonly client: NodeHandle;
readonly url: string;
readonly events: readonly string[];
readonly upstream: Upstream;
}
client(NodeHandle) — 이 단계에 연결된 클라이언트 단계.url(string) — 단계의 입력 항목이 지정하는 주소.events(readonly string[]) — 단계의 입력 항목이 지정하는 이벤트 유형.upstream(Upstream) — 바로 앞 상위 단계 각각의 출력.
AppInput
앱 단계의 입력: 매니페스트 입력 항목과 연결된 클라이언트.
export interface AppInput {
readonly client: NodeHandle;
readonly manifest: ManifestOptions;
readonly upstream: Upstream;
}
client(NodeHandle) — 이 단계에 연결된 클라이언트 단계.manifest(ManifestOptions) — 단계의 입력 항목이 설명하는 매니페스트.upstream(Upstream) — 바로 앞 상위 단계 각각의 출력.
InputFor
주어진 종류의 단계 스크립트가 받는 입력입니다.
export type InputFor<K extends Spec['kind']> = K extends 'client' ? ClientInput : K extends 'secret' ? SecretInput : K extends 'webhook' ? WebhookInput : AppInput;
LingaraV1
스크립트가 호출하는 헬퍼입니다. 각 헬퍼는 고정된(frozen) spec을 반환하며, 타입이 잘못된 옵션에는 그 옵션의 이름을 밝힌 TypeError를 던집니다.
export interface LingaraV1 {
readonly version: 'v1';
client(options: ClientOptions): ClientSpec;
secret(client: NodeHandle): SecretSpec;
webhook(options: WebhookOptions): WebhookSpec;
app(options: AppOptions): AppSpec;
manifest(options: ManifestOptions): Manifest;
}
version('v1') — 항상"v1".client(options: ClientOptions): ClientSpec— API 클라이언트.secret(client: NodeHandle): SecretSpec— 클라이언트 단계의 새 시크릿.webhook(options: WebhookOptions): WebhookSpec— 클라이언트 단계의 웹훅 엔드포인트.app(options: AppOptions): AppSpec— 클라이언트 단계의 앱.manifest(options: ManifestOptions): Manifest— 전송 형식의 매니페스트로, v1이 선택 사항으로 남겨 둔 옵션이 채워져 있습니다.
return lingara.client({ name: input.name, scopes: input.scopes })
return lingara.secret(input.client)
return lingara.webhook({ client: input.client, url: input.url, events: input.events })
return lingara.app({ client: input.client, manifest: lingara.manifest(input.manifest) })
NodeScript
단계의 스크립트는 이 함수의 본문입니다: return lingara.webhook({ client: input.client, url: input.url, events: input.events }).
export type NodeScript<K extends Spec['kind']> = (lingara: LingaraV1, input: InputFor<K>) => Spec;