sasjs-adapter

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

@sasjs/adapter

@sasjs/adapter

@sasjs/adapter
is the TypeScript library for calling SAS services/jobs from browsers or Node, with a unified API across three server types:
SAS9
,
SASVIYA
,
SASJS
.
@sasjs/adapter
是一个 TypeScript 库,用于从浏览器或 Node 调用 SAS 服务/作业,并为三种服务器类型提供统一 API:
SAS9
、
SASVIYA
、
SASJS
。

Basic setup

基本设置

ts
import SASjs from '@sasjs/adapter'

const sasjs = new SASjs({
  serverUrl: 'https://sas.example.com',
  serverType: 'SASVIYA',      // SAS9 | SASVIYA | SASJS
  appLoc: '/Public/app/myapp', // root folder of deployed services
  contextName: 'SAS Job Execution compute context', // Viya only
  debug: false
})
ts
import SASjs from '@sasjs/adapter'

const sasjs = new SASjs({
  serverUrl: 'https://sas.example.com',
  serverType: 'SASVIYA',      // SAS9 | SASVIYA | SASJS
  appLoc: '/Public/app/myapp', // root folder of deployed services
  contextName: 'SAS Job Execution compute context', // Viya only
  debug: false
})

request() - inputs (JS -> SAS)

request() - 输入(JS -> SAS)

Signature

函数签名

ts
sasjs.request(
  sasJob: string,
  data: { [key: string]: any[] } | null,
  config?: { [key: string]: any },             // merged over SASjsConfig
  loginRequiredCallback?: () => any,
  authConfig?: AuthConfig,                      // Viya tokens for Node usage
  extraResponseAttributes?: ExtraResponseAttributes[]  // ['log'] | ['file'] | ['data']
)
  • sasJob
    : relative path (no leading slash) resolved against
    appLoc
    , or an absolute path. Becomes the SAS
    _program
    parameter.
  • data
    : an object whose keys become work datasets in SAS. Can be
    null
    if the service takes no input.
  • config
    : overrides merged on top of the constructor config for this call only.
ts
sasjs.request(
  sasJob: string,
  data: { [key: string]: any[] } | null,
  config?: { [key: string]: any },             // merged over SASjsConfig
  loginRequiredCallback?: () => any,
  authConfig?: AuthConfig,                      // Viya tokens for Node usage
  extraResponseAttributes?: ExtraResponseAttributes[]  // ['log'] | ['file'] | ['data']
)
  • sasJob
    :相对路径(不含前导斜杠),会基于
    appLoc
    进行解析;也可以是绝对路径。它会成为 SAS 的
    _program
    参数。
  • data
    :一个对象,其键会成为 SAS 中的 work 数据集。如果服务不需要输入,可以为
    null
    。
  • config
    :仅在本次调用中覆盖并合并到构造函数配置之上的覆盖项。

The
data
object - tables, not arbitrary JSON

data
对象——是表,而不是任意 JSON

Every key in
data
must be an array of plain objects. Each key becomes a SAS work dataset named after the key; each object in the array is a row; each property is a column.
ts
const res = await sasjs.request('services/common/getdata', {
  customers: [
    { id: 1, name: 'Acme', active: true },
    { id: 2, name: 'Globex', active: false }
  ],
  config: [{ rootdir: '/tmp', retries: 3 }]  // even a single-row "config" table
})
On the SAS side these arrive as
work.customers
and
work.config
.
data
中的每个键都必须是 普通对象组成的数组。每个键会成为以该键命名的 SAS work 数据集;数组中的每个对象是一行;每个属性是一列。
ts
const res = await sasjs.request('services/common/getdata', {
  customers: [
    { id: 1, name: 'Acme', active: true },
    { id: 2, name: 'Globex', active: false }
  ],
  config: [{ rootdir: '/tmp', retries: 3 }]  // even a single-row "config" table
})
在 SAS 端,这些数据分别到达
work.customers
和
work.config
。

Input validation rules

输入验证规则

Before sending, the adapter validates
data
(see
validateInput
). Violations reject the promise with an
ErrorResponse
:
  • data
    must be
    null
    or a plain object (not an array, not a primitive).
  • Every table key must start with a letter or underscore:
    /^[a-zA-Z_][a-zA-Z0-9_]*$/
    . Numbers at the start are rejected.
  • Table names cannot exceed 32 characters (SAS name limit).
  • Every value under a key must be an array of objects. Non-object rows are rejected.
  • No property in any row may be
    undefined
    (it must be
    null
    , a string, a number, or a boolean).
发送前,adapter 会验证
data
(参见
validateInput
)。违反规则时,Promise 将以
ErrorResponse
拒绝:
  • data
    必须是
    null
    或普通对象(不能是数组或基本类型)。
  • 每个表键都必须以字母或下划线开头:
    /^[a-zA-Z_][a-zA-Z0-9_]*$/
    。以数字开头的名称会被拒绝。
  • 表名不能超过 32 个字符(SAS 名称长度限制)。
  • 每个键对应的值都必须是对象数组。包含非对象行的数据会被拒绝。
  • 任意行中的属性都不能为
    undefined
    (必须是
    null
    、字符串、数字或布尔值)。

How tables are serialized to CSV

表如何序列化为 CSV

Each table is converted to CSV via
convertToCSV
. The conversion is type-aware:
  • String columns: quoted only if they contain commas, tabs, newlines, or quotes. Internal
    "
    is escaped as
    ""
    .
  • Numeric columns: emitted unquoted.
  • Null / empty in a numeric column: becomes
    .
    (SAS missing).
  • Column type inference: the adapter scans all rows per column. If a column has both
    null
    /
    number
    values and special-missing strings (
    .a
    -
    .z
    ,
    _
    ), the column is typed as
    best.
    . Otherwise the first non-empty value determines
    chars
    vs
    number
    .
  • Column format header: the first CSV line is a space-delimited format spec, e.g.
    name:$char20. id:best.
    - the SAS side uses this to assign informats.
  • Byte-size length check: string values are measured in UTF-8 bytes. If any value exceeds 32765 bytes, the request throws
    The max length of a string value in SASjs is 32765 characters.
  • Formats tables: a key prefixed with
    $
    (e.g.
    $customers
    ) is treated as a formats definition for the
    customers
    table and is not sent as a separate dataset. Its
    formats
    property maps column names to SAS formats.
每张表都会通过
convertToCSV
转换为 CSV。该转换能够识别类型:
  • 字符串列:仅当字符串包含逗号、制表符、换行符或引号时才添加引号。内部的
    "
    会转义为
    ""
    。
  • 数值列:不加引号直接输出。
  • 数值列中的 null/空值:转换为
    .
    (SAS 缺失值)。
  • 列类型推断:adapter 会扫描每列的所有行。如果某列同时包含
    null
    /数字值和特殊缺失值字符串(
    .a
    -
    .z
    、
    _
    ),则该列的类型为
    best.
    。否则,由第一个非空值决定列类型是
    chars
    还是
    number
    。
  • 列格式标题:CSV 的第一行是使用空格分隔的格式规范,例如
    name:$char20. id:best.
    。SAS 端会使用它来指定 informat。
  • 字节大小长度检查:字符串值按 UTF-8 字节计算长度。如果任何值超过 32765 字节,请求将抛出
    The max length of a string value in SASjs is 32765 characters.
  • Formats 表:以
    $
    为前缀的键(例如
    $customers
    )会被视为
    customers
    表的 formats 定义,而不会作为单独的数据集发送。其
    formats
    属性用于将列名映射到 SAS format。

Two transport modes

两种传输模式

The adapter picks the transport based on payload size and server type (see
WebJobExecutor
):
  1. Param-based (default for Viya web / SASjs server, small payloads): each table's CSV is appended as a form field named
    sasjs{N}data
    (N = 1, 2, ...). A
    sasjs_tables
    field lists the table names space-separated. If a single CSV exceeds 16000 chars it is split into chunks:
    sasjs{N}data0
    holds the chunk count,
    sasjs{N}data1..N
    hold the pieces.
  2. File-upload (SAS 9 always; Viya/SASjs when
    JSON.stringify(data)
    exceeds 500000 chars or contains a
    ;
    ): each table is appended as a CSV file (
    {tableName}.csv
    ) in multipart form data.
In both cases the body is
multipart/form-data
with debug params (
_debug=131
,
_omittextlog=false
,
_omitSessionResults=false
) when
debug: true
.
adapter 会根据负载大小和服务器类型选择传输方式(参见
WebJobExecutor
):
  1. 基于参数(Viya Web/SASJS 服务器处理小型负载时的默认方式):每张表的 CSV 会作为名为
    sasjs{N}data
    的表单字段附加,其中 N = 1、2、……。
    sasjs_tables
    字段会列出以空格分隔的表名。如果单个 CSV 超过 16000 个字符,则会对其进行分块:
    sasjs{N}data0
    保存分块数量,
    sasjs{N}data1..N
    保存各个分块。
  2. 文件上传(SAS 9 始终使用此方式;当
    JSON.stringify(data)
    超过 500000 个字符或包含
    ;
    时,Viya/SASJS 也使用此方式):每张表都会作为 CSV 文件(
    {tableName}.csv
    )附加到 multipart form data 中。
在这两种情况下,请求体都是
multipart/form-data
;当
debug: true
时,还会包含调试参数(
_debug=131
、
_omittextlog=false
、
_omitSessionResults=false
)。

request() - outputs (SAS -> JS)

request() - 输出(SAS -> JS)

Response shape depends on
extraResponseAttributes

响应结构取决于
extraResponseAttributes

Default (no
extraResponseAttributes
): the resolved value is the parsed webout object directly - whatever JSON the SAS service wrote to
_webout
:
ts
// SAS service wrote: {"mydata":[{"COL1":"x","COL2":1}]}
const res = await sasjs.request('services/getdata', { in: [{ a: 1 }] })
// res === { mydata: [{ COL1: 'x', COL2: 1 }] }
// res.mydata[0].COL1  // note UPPERCASE column names
With
extraResponseAttributes
(e.g.
['log']
): the result is wrapped:
ts
const res = await sasjs.request('services/getdata', { in: [{ a: 1 }] }, {}, undefined, undefined, ['log'])
// res === { result: { mydata: [...] }, log: '<sas log string>' }
ExtraResponseAttributes
is
'file' | 'data' | 'log'
. Only
log
is fully implemented in current releases.
默认情况(未设置
extraResponseAttributes
):解析后的值就是 webout 对象本身,即 SAS 服务写入
_webout
的 JSON:
ts
// SAS service wrote: {"mydata":[{"COL1":"x","COL2":1}]}
const res = await sasjs.request('services/getdata', { in: [{ a: 1 }] })
// res === { mydata: [{ COL1: 'x', COL2: 1 }] }
// res.mydata[0].COL1  // note UPPERCASE column names
设置
extraResponseAttributes
(例如
['log']
)后,结果会被包装:
ts
const res = await sasjs.request('services/getdata', { in: [{ a: 1 }] }, {}, undefined, undefined, ['log'])
// res === { result: { mydata: [...] }, log: '<sas log string>' }
ExtraResponseAttributes
可以是
'file' | 'data' | 'log'
。在当前版本中,只有
log
已完整实现。

Name casing

名称大小写

  • Table names in the response are lowercase - this is enforced by the
    %webout
    /
    mp_jsonout
    macro on the SAS side, not the adapter. The adapter passes the webout JSON through as-is.
  • Column names are UPPERCASE - SAS dataset variable names are uppercased by SAS itself, and the adapter does not re-case them.
  • 响应中的 表名为 小写。这是 SAS 端的
    %webout
    /
    mp_jsonout
    宏强制执行的,并非由 adapter 处理。adapter 会原样传递 webout JSON。
  • 列名为 大写。SAS 数据集变量名本身会被 SAS 转换为大写,adapter 不会再次转换大小写。

What the SAS service must produce

SAS 服务必须生成的内容

The service must write valid JSON to
_webout
. Conventionally via
%webout
(from @sasjs/core), which wraps
mp_jsonout
. The JSON shape is an object whose keys are table names, each mapping to an array of row objects:
sas
%webout(OPEN)
%webout(OBJ, work.customers)   /* -> {"customers":[{...}]} */
%webout(CLOSE)
服务必须向
_webout
写入有效的 JSON。通常可使用
%webout
(来自
@sasjs/core
),它会包装
mp_jsonout
。JSON 的结构应为:对象中的键是表名,每个表名对应一个行对象数组:
sas
%webout(OPEN)
%webout(OBJ, work.customers)   /* -> {"customers":[{"..."}]} */
%webout(CLOSE)

Debug mode

调试模式

When
config.debug
is
true
:
  • SAS 9: the raw response is a string containing the log plus the webout, delimited by
    >>weboutBEGIN<<
    and
    >>weboutEND<<
    . The adapter extracts the JSON between the markers.
  • Viya (web/JES): the debug response is parsed via
    parseSasViyaDebugResponse
    /
    parseSasViyaLogDebugResponse
    , which split out the log and the webout JSON.
  • Viya (compute API): the log is fetched separately from the compute session.
In all cases, with debug on and
extraResponseAttributes: ['log']
, the SAS log is available in
res.log
.
当
config.debug
为
true
时:
  • SAS 9:原始响应是一个字符串,其中包含日志和 webout,并以
    >>weboutBEGIN<<
    和
    >>weboutEND<<
    分隔。adapter 会提取标记之间的 JSON。
  • Viya(Web/JES):通过
    parseSasViyaDebugResponse
    /
    parseSasViyaLogDebugResponse
    解析调试响应,将日志和 webout JSON 分离。
  • Viya(Compute API):日志会从 compute session 中单独获取。
在所有情况下,启用调试并设置
extraResponseAttributes: ['log']
后,都可以通过
res.log
获取 SAS 日志。

Request history

请求历史

Every executed request is appended to an in-memory history (capped at
requestHistoryLimit
, default 10):
ts
sasjs.getSasRequests()
// SASjsRequest[]: { serviceLink, timestamp, sourceCode, generatedCode, logFile, SASWORK }
sourceCode
and
generatedCode
are only populated when
debug: true
. The history is a ring buffer - oldest entries are dropped when the limit is exceeded.
每个已执行的请求都会追加到内存中的历史记录中,其上限由
requestHistoryLimit
控制,默认值为 10:
ts
sasjs.getSasRequests()
// SASjsRequest[]: { serviceLink, timestamp, sourceCode, generatedCode, logFile, SASWORK }
仅当
debug: true
时,
sourceCode
和
generatedCode
才会包含数据。历史记录采用环形缓冲区;超过上限后,最旧的记录会被丢弃。

Authentication

身份验证

  • SAS 9:
    sasjs.logIn(username, password)
    (form-based against the stored process server). Session cookie is managed automatically.
  • Viya: OAuth client/secret (client credentials grant) or authorization code flow; tokens are refreshed automatically. Configure via CLI (
    sasjs add cred
    ) for Node usage. For Node, pass
    authConfig
    (client/secret/access/refresh tokens) to
    request()
    .
  • SASJS server: token-based auth against the sasjs/server API.
  • SAS 9:
    sasjs.logIn(username, password)
    ,使用基于表单的方式针对已存储的 process server 进行身份验证。Session cookie 会自动管理。
  • Viya:支持 OAuth client/secret(client credentials grant)或 authorization code flow;token 会自动刷新。在 Node 中使用时,可通过 CLI(
    sasjs add cred
    )进行配置。在 Node 中调用
    request()
    时,可传入
    authConfig
    (client/secret/access/refresh token)。
  • SASJS 服务器:使用基于 token 的方式对 sasjs/server API 进行身份验证。

Key classes / modules

关键类/模块

  • SASjs
    - main facade:
    request()
    ,
    logIn()/logOut()
    ,
    uploadFile()
    ,
    executeScript()
    ,
    startComputeJob()
    ,
    deployServicePack()
  • RequestClient
    - HTTP layer; holds the request history (
    getRequests()
    ), CSRF tokens, and auth headers
  • WebJobExecutor
    - SAS 9 STP, Viya web JES, and SASjs server web (multipart form POST)
  • ComputeJobExecutor
    - Viya compute API (direct code submission on a named context)
  • JesJobExecutor
    - Viya JES API (job submission via the jobs API, not the web app)
  • SasjsJobExecutor
    - SASjs server web execution
  • SessionManager
    - Viya compute session lifecycle
  • SASViyaApiClient
    /
    SAS9ApiClient
    /
    SASjsApiClient
    - low-level per-platform clients (rarely needed directly)
  • file/
    utilities - file upload to SAS (binary content handling)
  • SASjs
    :主入口类(facade),提供
    request()
    、
    logIn()/logOut()
    、
    uploadFile()
    、
    executeScript()
    、
    startComputeJob()
    、
    deployServicePack()
    。
  • RequestClient
    :HTTP 层;保存请求历史(
    getRequests()
    )、CSRF token 和身份验证请求头。
  • WebJobExecutor
    :处理 SAS 9 STP、Viya Web JES 和 SASJS 服务器 Web 执行(multipart form POST)。
  • ComputeJobExecutor
    :处理 Viya Compute API(在指定 context 上直接提交代码)。
  • JesJobExecutor
    :处理 Viya JES API(通过 jobs API 提交作业,而不是使用 Web 应用)。
  • SasjsJobExecutor
    :处理 SASJS 服务器 Web 执行。
  • SessionManager
    :管理 Viya compute session 的生命周期。
  • SASViyaApiClient
    /
    SAS9ApiClient
    /
    SASjsApiClient
    :各平台底层客户端,通常无需直接使用。
  • file/
    工具:向 SAS 上传文件(处理二进制内容)。

Tips

提示

  • Set
    debug: true
    to surface the SAS log in responses while developing.
  • Always handle
    response.status
    / error responses - SAS-side errors (e.g. from
    %mp_abort
    ) come back in the JSON, not necessarily as HTTP errors.
  • For large payloads prefer CSV upload or streamed files over JSON input tables.
  • Keep
    appLoc
    consistent with the
    appLoc
    in
    sasjsconfig.json
    used to deploy.
  • Column names come back UPPERCASE from SAS. If your frontend expects lowercase, map them in JS rather than trying to force SAS casing.
  • null
    in a numeric input column becomes SAS missing (
    .
    ); empty string in a char column becomes empty string. Don't use
    undefined
    - validation rejects it.
  • 开发期间可设置
    debug: true
    ,以便在响应中显示 SAS 日志。
  • 始终处理
    response.status
    /错误响应。SAS 端错误(例如
    %mp_abort
    产生的错误)会出现在 JSON 中,不一定会作为 HTTP 错误返回。
  • 对于大型负载,应优先使用 CSV 上传或流式文件,而不是 JSON 输入表。
  • 保持
    appLoc
    与部署时使用的
    sasjsconfig.json
    中的
    appLoc
    一致。
  • SAS 返回的列名为大写。如果前端需要小写列名,应在 JS 中进行映射,而不是尝试强制改变 SAS 的大小写。
  • 数值输入列中的
    null
    会转换为 SAS 缺失值(
    .
    );字符列中的空字符串会保持为空字符串。不要使用
    undefined
    ,验证会直接拒绝该值。

Using the adapter without a bundler (zero-build / strict CSP frontends)

不使用打包工具时使用 adapter(零构建/严格 CSP 前端)

The package root
index.js
is a UMD bundle exposing a global
SASjs
. Pattern (from the minimal seed app):
  1. "prepare": "cp node_modules/@sasjs/adapter/index.js src/sasjs.js"
    in package.json (runs on
    npm i
    ).
  2. <script src="sasjs.js"></script>
    before your app script.
  3. Configure via a hidden custom element:
    <sasjs serverType="SASJS" appLoc="/Public/app/myapp" debug="false"></sasjs>
    and read attributes with
    document.querySelector('sasjs')
    . When the app is streamed by SAS itself, omit
    serverUrl
    - same-origin requests just work (CSP
    default-src 'self'
    safe).
包根目录下的
index.js
是一个暴露全局
SASjs
的 UMD bundle。使用方式如下(来自最小种子应用):
  1. 在 package.json 中设置
    "prepare": "cp node_modules/@sasjs/adapter/index.js src/sasjs.js"
    (在执行
    npm i
    时运行)。
  2. 在应用脚本之前添加
    <script src="sasjs.js"></script>
    。
  3. 通过隐藏的自定义元素进行配置:
    <sasjs serverType="SASJS" appLoc="/Public/app/myapp" debug="false"></sasjs>
    ,并使用
    document.querySelector('sasjs')
    读取属性。当应用由 SAS 自身流式提供时,可以省略
    serverUrl
    ——同源请求可直接正常工作(CSP
    default-src 'self'
    安全)。

Limitations

限制

This skill is a static reference for the @sasjs/adapter library - it provides guidance on the API, request/response shapes, authentication, and CSV serialization. It does not execute code, run shell commands, access the filesystem, connect to databases, or make network requests. All code examples are illustrative; the user must integrate them into their own application. References to authentication tokens describe what the adapter library manages at runtime - this skill does not read, write, or access those values itself.
本 skill 是
@sasjs/adapter
库的静态参考资料,用于提供 API、请求/响应结构、身份验证和 CSV 序列化方面的指导。它不会执行代码、运行 shell 命令、访问文件系统、连接数据库或发起网络请求。所有代码示例仅作说明,用户必须将其集成到自己的应用中。关于身份验证 token 的描述仅说明 adapter 库在运行时管理的功能;本 skill 本身不会读取、写入或访问这些值。