From bac9ee99a62221f2314d7bc437401bf38fa05c88 Mon Sep 17 00:00:00 2001 From: Misato Takahashi Date: Mon, 27 Apr 2026 10:54:37 +0900 Subject: [PATCH] feat: add iconv runtime wrapper Add @tailor-platform/sdk/iconv as a typed wrapper around the platform's tailor.iconv runtime API for character encoding conversion (Shift_JIS, EUC-JP, IBM EBCDIC variants, etc.). Includes setupIconvMock for unit tests and a docs page mirroring the platform reference. Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/iconv-runtime-wrapper.md | 5 + packages/sdk/README.md | 4 + packages/sdk/docs/iconv.md | 174 +++++++++++++++++++++++++++ packages/sdk/package.json | 5 + packages/sdk/src/iconv/index.test.ts | 107 ++++++++++++++++ packages/sdk/src/iconv/index.ts | 111 +++++++++++++++++ packages/sdk/src/utils/test/index.ts | 1 + packages/sdk/src/utils/test/mock.ts | 98 ++++++++++++++- packages/sdk/tsdown.config.ts | 1 + skills/tailor-sdk/SKILL.md | 2 + 10 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 .changeset/iconv-runtime-wrapper.md create mode 100644 packages/sdk/docs/iconv.md create mode 100644 packages/sdk/src/iconv/index.test.ts create mode 100644 packages/sdk/src/iconv/index.ts diff --git a/.changeset/iconv-runtime-wrapper.md b/.changeset/iconv-runtime-wrapper.md new file mode 100644 index 000000000..22022400d --- /dev/null +++ b/.changeset/iconv-runtime-wrapper.md @@ -0,0 +1,5 @@ +--- +"@tailor-platform/sdk": minor +--- + +Add `@tailor-platform/sdk/iconv` runtime wrapper for character encoding conversion. Exports typed `convert`, `convertBuffer`, `decode`, `encode`, `encodings`, and `Iconv` class that delegate to the platform's `tailor.iconv` runtime API. Use `setupIconvMock()` from `@tailor-platform/sdk/test` to mock these calls in unit tests. diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 816587aca..7e5e8179e 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -83,6 +83,10 @@ the installed SDK version. Files are copied (not symlinked) so they survive | [Static Website](./docs/services/staticwebsite.md) | Static file hosting | | [Secret Manager](./docs/services/secret.md) | Secure credential storage | +### Runtime Utilities + +- [Character Encoding Conversion (iconv)](./docs/iconv.md) - Convert between UTF-8, Shift_JIS, EUC-JP, IBM EBCDIC, and other encodings via `@tailor-platform/sdk/iconv` + ### Guides - [Testing Guide](./docs/testing.md) - Unit and E2E testing patterns diff --git a/packages/sdk/docs/iconv.md b/packages/sdk/docs/iconv.md new file mode 100644 index 000000000..777dc13b3 --- /dev/null +++ b/packages/sdk/docs/iconv.md @@ -0,0 +1,174 @@ +# Character Encoding Conversion (iconv) + +`@tailor-platform/sdk/iconv` is a thin typed wrapper around the platform-provided `tailor.iconv` runtime API. It enables conversion between character encodings — useful for handling Shift_JIS / EUC-JP CSV imports, integrating with mainframes that use IBM EBCDIC variants, or normalizing legacy data into UTF-8. + +For the full list of supported encodings and platform-side details, see the official [Character Encoding Conversion](https://docs.tailor.tech/reference/concepts/character-encodings.html) reference. + +## Overview + +The module provides: + +- Stateless functions for one-off conversions: `convert`, `convertBuffer`, `decode`, `encode`, `encodings` +- A stateful `Iconv` class for repeated conversions between a fixed encoding pair (compatible with the `node-iconv` API surface) +- A typed test mock helper, `setupIconvMock`, for unit tests + +All functions and the `Iconv` class delegate to `globalThis.tailor.iconv` at runtime, which is provided by the Tailor Platform Function runtime. They are intended for use inside resolvers, executors, and workflow jobs. + +## Supported Encodings + +Common encodings include: + +- **Unicode**: `UTF-8`, `UTF-16`, `UTF-16BE`, `UTF-16LE` +- **Japanese**: `Shift_JIS` (aliases: `SJIS`, `CP932`), `EUC-JP`, `EUC-JP-MS`, `ISO-2022-JP` +- **Enterprise / mainframe**: IBM EBCDIC variants (`IBM037`, `IBM290`, `IBM930`, `IBM939`, `IBM943`), Hitachi KEIS, NEC JIS aliases +- **Chinese**: `GB2312`, `GBK`, `GB18030`, `Big5`, `BIG5HKSCS` +- **Korean**: `EUC-KR`, `UHC`, `JOHAB`, `ISO-2022-KR` +- **Other**: `ISO-8859-1`, `ASCII` + +Call `encodings()` at runtime to get the full list supported by the platform. + +## API + +### `convert(data, fromEncoding, toEncoding)` + +Convert a string or buffer between encodings. The return type narrows based on `toEncoding`: it is `string` when `toEncoding` is `"UTF-8"` or `"UTF8"`, and `Uint8Array` otherwise. + +```typescript +import { convert } from "@tailor-platform/sdk/iconv"; + +// UTF-8 string → Shift_JIS bytes +const sjisBytes = convert("日本語テキスト", "UTF-8", "Shift_JIS"); +// ^? Uint8Array + +// EUC-JP bytes → UTF-8 string +const utf8Text = convert(eucjpBuffer, "EUC-JP", "UTF-8"); +// ^? string +``` + +### `convertBuffer(buffer, fromEncoding, toEncoding)` + +Like `convert`, but accepts only a `Uint8Array | ArrayBuffer` input. Use this when you want the type system to enforce buffer input. + +### `decode(buffer, encoding)` + +Decode a buffer into a UTF-8 string by interpreting it with the given source encoding. Equivalent to `convert(buffer, encoding, "UTF-8")`. + +```typescript +import { decode } from "@tailor-platform/sdk/iconv"; + +const text = decode(sjisCsvBuffer, "Shift_JIS"); // string +``` + +### `encode(str, encoding)` + +Encode a UTF-8 string into the given target encoding. Returns `string` when the target is UTF-8, otherwise `Uint8Array`. + +```typescript +import { encode } from "@tailor-platform/sdk/iconv"; + +const sjisBytes = encode("こんにちは", "Shift_JIS"); // Uint8Array +``` + +### `encodings()` + +Return the list of supported encoding identifiers from the runtime. + +```typescript +import { encodings } from "@tailor-platform/sdk/iconv"; + +const list = encodings(); // string[] +``` + +### `Iconv` class + +Stateful converter for repeated conversions between a fixed encoding pair. Useful when you process many records with the same source/target encoding and want to avoid passing the encoding pair on every call. + +```typescript +import { Iconv } from "@tailor-platform/sdk/iconv"; + +const conv = new Iconv("Shift_JIS", "UTF-8"); +for (const row of sjisRows) { + const utf8 = conv.convert(row); // string | Uint8Array +} +``` + +## Error Handling Flags + +Append flags to `toEncoding` to control behavior on unconvertible characters: + +| Flag | Behavior | +| ----------------- | --------------------------------------------------------------------------------- | +| `//IGNORE` | Silently skip characters that cannot be represented in the target encoding | +| `//TRANSLIT` | Replace unconvertible characters with `?` (default substitute) | +| `//TRANSLIT:char` | Replace unconvertible characters with the specified replacement (e.g. `*`, `[?]`) | + +```typescript +import { convert } from "@tailor-platform/sdk/iconv"; + +convert("Hello 世界!", "UTF-8", "ASCII//TRANSLIT:*"); +// → "Hello **!" + +convert("Test 日本語", "UTF-8", "ASCII//TRANSLIT:[?]"); +// → "Test [?][?][?]" +``` + +## Usage in a Resolver + +A common pattern is to fetch bytes from a TailorDB file field, decode them, and process the result. Bytes can come from `tailordb.file.download` (or a generated helper from the [`file-utils` plugin](./plugin/index.md)), an external HTTP fetch, or a base64-encoded input. + +```typescript +import { createResolver, t } from "@tailor-platform/sdk"; +import { decode } from "@tailor-platform/sdk/iconv"; + +export default createResolver({ + name: "importSjisCsv", + operation: "mutation", + input: { csvBase64: t.string() }, + output: { rows: t.int() }, + body: async ({ input }) => { + const bytes = Uint8Array.from(atob(input.csvBase64), (c) => c.charCodeAt(0)); + const text = decode(bytes, "Shift_JIS"); + const rows = text.split("\n").filter((line) => line.length > 0).length; + // ...persist parsed rows + return { rows }; + }, +}); +``` + +## Testing + +Use `setupIconvMock()` from `@tailor-platform/sdk/test` to mock `tailor.iconv` in unit tests. The default implementation passes strings through and uses Node's `TextEncoder`/`TextDecoder` for UTF-8, which is enough for most assertions. For non-UTF-8 round trips, supply your own handler via `onConvert`. + +```typescript +import { afterEach, describe, expect, test } from "vitest"; +import { setupIconvMock, unauthenticatedTailorUser } from "@tailor-platform/sdk/test"; +import resolver from "./resolvers/importSjisCsv"; + +const TailorGlobal = globalThis as { tailor?: { iconv?: unknown } }; + +describe("importSjisCsv resolver", () => { + afterEach(() => { + delete TailorGlobal.tailor; + }); + + test("decodes Shift_JIS CSV", async () => { + const { calls } = setupIconvMock({ + onDecode: (_buffer, encoding) => { + expect(encoding).toBe("Shift_JIS"); + return "name,age\nAlice,30\n"; + }, + }); + + const result = await resolver.body({ + input: { csvBase64: btoa("dummy bytes") }, + user: unauthenticatedTailorUser, + env: {}, + }); + + expect(result).toEqual({ rows: 2 }); + expect(calls).toHaveLength(1); + }); +}); +``` + +`setupIconvMock` records every call in the returned `calls` array (`{ method, args }`) so you can assert that the right encoding was requested. Clean up by deleting `TailorGlobal.tailor` in `afterEach`. diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 857152ff9..d0e4e7b61 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -44,6 +44,11 @@ "import": "./dist/kysely/index.mjs", "default": "./dist/kysely/index.mjs" }, + "./iconv": { + "types": "./dist/iconv/index.d.mts", + "import": "./dist/iconv/index.mjs", + "default": "./dist/iconv/index.mjs" + }, "./plugin": { "types": "./dist/plugin/index.d.mts", "import": "./dist/plugin/index.mjs", diff --git a/packages/sdk/src/iconv/index.test.ts b/packages/sdk/src/iconv/index.test.ts new file mode 100644 index 000000000..c05e6f984 --- /dev/null +++ b/packages/sdk/src/iconv/index.test.ts @@ -0,0 +1,107 @@ +import { afterEach, describe, it, expect, expectTypeOf } from "vitest"; +import { setupIconvMock } from "@/utils/test/mock"; +import { convert, convertBuffer, decode, encode, encodings, Iconv } from "./index"; + +const TailorGlobal = globalThis as { tailor?: { iconv?: unknown } }; + +describe("@tailor-platform/sdk/iconv", () => { + afterEach(() => { + delete TailorGlobal.tailor; + }); + + describe("convert", () => { + it("delegates to tailor.iconv.convert", () => { + const { calls } = setupIconvMock(); + const result = convert("hello", "UTF-8", "Shift_JIS"); + expect(result).toBeInstanceOf(Uint8Array); + expect(calls).toEqual([{ method: "convert", args: ["hello", "UTF-8", "Shift_JIS"] }]); + }); + + it("returns string when toEncoding is UTF-8", () => { + setupIconvMock({ + onConvert: (input, _from, to) => { + if (to === "UTF-8") return "decoded"; + return new Uint8Array([1, 2, 3]); + }, + }); + const result = convert(new Uint8Array([0xe3, 0x81, 0x82]), "Shift_JIS", "UTF-8"); + expect(result).toBe("decoded"); + }); + + it("type narrows return based on toEncoding literal", () => { + setupIconvMock(); + // Type-level checks — only reachable when iconv mock is set up at runtime. + expectTypeOf(convert("a", "UTF-8", "UTF-8")).toEqualTypeOf(); + expectTypeOf(convert("a", "UTF-8", "UTF8")).toEqualTypeOf(); + expectTypeOf(convert("a", "UTF-8", "Shift_JIS")).toEqualTypeOf(); + }); + }); + + describe("convertBuffer", () => { + it("delegates to tailor.iconv.convertBuffer", () => { + const { calls } = setupIconvMock(); + const buf = new Uint8Array([1, 2, 3]); + convertBuffer(buf, "Shift_JIS", "UTF-8"); + expect(calls).toEqual([{ method: "convertBuffer", args: [buf, "Shift_JIS", "UTF-8"] }]); + }); + }); + + describe("decode", () => { + it("decodes a buffer to a UTF-8 string", () => { + const { calls } = setupIconvMock(); + const buf = new TextEncoder().encode("hello"); + const result = decode(buf, "UTF-8"); + expect(result).toBe("hello"); + expect(calls).toEqual([{ method: "decode", args: [buf, "UTF-8"] }]); + }); + }); + + describe("encode", () => { + it("encodes a string to a buffer", () => { + const { calls } = setupIconvMock(); + const result = encode("hello", "Shift_JIS"); + expect(result).toBeInstanceOf(Uint8Array); + expect(calls).toEqual([{ method: "encode", args: ["hello", "Shift_JIS"] }]); + }); + + it("returns string when encoding is UTF-8", () => { + setupIconvMock(); + expectTypeOf(encode("a", "UTF-8")).toEqualTypeOf(); + expectTypeOf(encode("a", "Shift_JIS")).toEqualTypeOf(); + }); + }); + + describe("encodings", () => { + it("returns the platform's supported encoding list", () => { + const { calls } = setupIconvMock({ onEncodings: () => ["UTF-8", "FOO"] }); + expect(encodings()).toEqual(["UTF-8", "FOO"]); + expect(calls).toEqual([{ method: "encodings", args: [] }]); + }); + }); + + describe("Iconv class", () => { + it("constructs and converts via the platform Iconv class", () => { + const { calls } = setupIconvMock(); + const conv = new Iconv("Shift_JIS", "UTF-8"); + const result = conv.convert(new Uint8Array([0xe3, 0x81, 0x82])); + expect(typeof result).toBe("string"); + expect(calls).toHaveLength(1); + expect(calls[0]?.method).toBe("convert"); + }); + + it("reuses fixed encoding pair across calls", () => { + const { calls } = setupIconvMock(); + const conv = new Iconv("UTF-8", "Shift_JIS"); + conv.convert("a"); + conv.convert("b"); + expect(calls).toHaveLength(2); + expect(calls[0]?.args[1]).toBe("UTF-8"); + expect(calls[0]?.args[2]).toBe("Shift_JIS"); + expect(calls[1]?.args[1]).toBe("UTF-8"); + }); + }); + + it("throws a clear runtime error when tailor.iconv is not available", () => { + expect(() => convert("a", "UTF-8", "UTF-8")).toThrow(); + }); +}); diff --git a/packages/sdk/src/iconv/index.ts b/packages/sdk/src/iconv/index.ts new file mode 100644 index 000000000..7ba834d38 --- /dev/null +++ b/packages/sdk/src/iconv/index.ts @@ -0,0 +1,111 @@ +/// + +/** + * Character encoding conversion utilities. + * + * Thin typed wrapper around the platform-provided `tailor.iconv` runtime API. + * At runtime this delegates to `globalThis.tailor.iconv`, which is provided by + * the Tailor Platform Function runtime. Use `setupIconvMock()` from + * `@tailor-platform/sdk/test` to mock these calls in unit tests. + * @example + * import { convert, decode, encode, Iconv } from "@tailor-platform/sdk/iconv"; + * + * const utf8 = convert(sjisBuffer, "Shift_JIS", "UTF-8"); // string + * const sjis = convert("こんにちは", "UTF-8", "Shift_JIS"); // Uint8Array + * + * const iconv = new Iconv("Shift_JIS", "UTF-8"); + * const out = iconv.convert(sjisBuffer); + */ + +/** + * Convert a string or buffer between encodings. + * Returns `string` when `toEncoding` is `"UTF8"` or `"UTF-8"`, otherwise `Uint8Array`. + * @param str - Input data + * @param fromEncoding - Source encoding + * @param toEncoding - Target encoding + * @returns Converted string or buffer + */ +export function convert( + str: string | Uint8Array | ArrayBuffer, + fromEncoding: string, + toEncoding: T, +): T extends "UTF8" | "UTF-8" ? string : Uint8Array { + return tailor.iconv.convert(str, fromEncoding, toEncoding); +} + +/** + * Convert a buffer between encodings. + * Returns `string` when `toEncoding` is `"UTF8"` or `"UTF-8"`, otherwise `Uint8Array`. + * @param buffer - Input buffer + * @param fromEncoding - Source encoding + * @param toEncoding - Target encoding + * @returns Converted string or buffer + */ +export function convertBuffer( + buffer: Uint8Array | ArrayBuffer, + fromEncoding: string, + toEncoding: T, +): T extends "UTF8" | "UTF-8" ? string : Uint8Array { + return tailor.iconv.convertBuffer(buffer, fromEncoding, toEncoding); +} + +/** + * Decode a buffer to a UTF-8 string using the given source encoding. + * @param buffer - Input buffer + * @param encoding - Source encoding of the buffer + * @returns Decoded string + */ +export function decode(buffer: Uint8Array | ArrayBuffer, encoding: string): string { + return tailor.iconv.decode(buffer, encoding); +} + +/** + * Encode a UTF-8 string into the given target encoding. + * Returns `string` when `encoding` is `"UTF8"` or `"UTF-8"`, otherwise `Uint8Array`. + * @param str - Input string (UTF-8) + * @param encoding - Target encoding + * @returns Encoded buffer or string + */ +export function encode( + str: string, + encoding: T, +): T extends "UTF8" | "UTF-8" ? string : Uint8Array { + return tailor.iconv.encode(str, encoding); +} + +/** + * Returns the list of supported encoding names. + * @returns Array of supported encoding identifiers + */ +export function encodings(): string[] { + return tailor.iconv.encodings(); +} + +interface IconvImpl { + convert(input: string | Uint8Array | ArrayBuffer): string | Uint8Array; +} + +/** + * Stateful converter for repeated conversions between a fixed encoding pair. + * Compatible with the `node-iconv` API surface. + */ +export class Iconv { + private impl: IconvImpl; + + /** + * @param fromEncoding - Source encoding + * @param toEncoding - Target encoding + */ + constructor(fromEncoding: string, toEncoding: string) { + this.impl = new tailor.iconv.Iconv(fromEncoding, toEncoding); + } + + /** + * Convert input using this converter's fixed encoding pair. + * @param input - Input data + * @returns Converted string or buffer + */ + convert(input: string | Uint8Array | ArrayBuffer): string | Uint8Array { + return this.impl.convert(input); + } +} diff --git a/packages/sdk/src/utils/test/index.ts b/packages/sdk/src/utils/test/index.ts index 128ff88ad..f0c090b52 100644 --- a/packages/sdk/src/utils/test/index.ts +++ b/packages/sdk/src/utils/test/index.ts @@ -9,6 +9,7 @@ export { setupTailorErrorsMock, setupWorkflowMock, setupWaitPointMock, + setupIconvMock, createImportMain, } from "./mock"; diff --git a/packages/sdk/src/utils/test/mock.ts b/packages/sdk/src/utils/test/mock.ts index 38cbe8442..4f45a83ab 100644 --- a/packages/sdk/src/utils/test/mock.ts +++ b/packages/sdk/src/utils/test/mock.ts @@ -11,6 +11,12 @@ type ResolveHandler = ( callback: (payload: unknown) => unknown, ) => Promise | void; +type IconvHandler = ( + input: string | Uint8Array | ArrayBuffer, + fromEncoding: string, + toEncoding: string, +) => string | Uint8Array; + interface TailordbGlobal { tailordb?: { Client: new (config: { namespace?: string }) => { @@ -23,7 +29,7 @@ interface TailordbGlobal { }; }; tailor?: { - workflow: { + workflow?: { triggerJobFunction: (jobName: string, args: unknown) => unknown; wait?: (key: string, payload?: unknown) => unknown; resolve?: ( @@ -32,6 +38,21 @@ interface TailordbGlobal { callback: (payload: unknown) => unknown, ) => Promise; }; + iconv?: { + convert: IconvHandler; + convertBuffer: ( + buffer: Uint8Array | ArrayBuffer, + fromEncoding: string, + toEncoding: string, + ) => string | Uint8Array; + decode: (buffer: Uint8Array | ArrayBuffer, encoding: string) => string; + encode: (str: string, encoding: string) => string | Uint8Array; + encodings: () => string[]; + Iconv: new ( + fromEncoding: string, + toEncoding: string, + ) => { convert(input: string | Uint8Array | ArrayBuffer): string | Uint8Array }; + }; }; } @@ -180,6 +201,81 @@ export function setupWaitPointMock(config?: { onWait?: WaitHandler; onResolve?: return { waitCalls, resolveCalls }; } +interface IconvMockConfig { + /** Handler for `convert` and `convertBuffer`. Defaults to passing input through unchanged. */ + onConvert?: IconvHandler; + /** Handler for `decode`. Defaults to UTF-8 TextDecoder. */ + onDecode?: (buffer: Uint8Array | ArrayBuffer, encoding: string) => string; + /** Handler for `encode`. Defaults to UTF-8 TextEncoder. */ + onEncode?: (str: string, encoding: string) => string | Uint8Array; + /** Handler for `encodings`. Defaults to a small static list. */ + onEncodings?: () => string[]; +} + +interface IconvCall { + method: "convert" | "convertBuffer" | "decode" | "encode" | "encodings"; + args: unknown[]; +} + +/** + * Sets up a mock for `globalThis.tailor.iconv` used in unit tests of code that + * imports from `@tailor-platform/sdk/iconv`. Defaults pass strings through and + * use Node's TextEncoder/TextDecoder for UTF-8. + * @param config - Optional handlers to override default behaviors. + * @returns Object containing an array of recorded calls for assertions. + */ +export function setupIconvMock(config?: IconvMockConfig): { calls: IconvCall[] } { + const calls: IconvCall[] = []; + const decoder = new TextDecoder(); + const encoder = new TextEncoder(); + + const defaultConvert: IconvHandler = (input, _from, to) => { + if (to === "UTF8" || to === "UTF-8") { + return typeof input === "string" ? input : decoder.decode(input); + } + return typeof input === "string" ? encoder.encode(input) : new Uint8Array(input); + }; + + GlobalThis.tailor = { + ...GlobalThis.tailor, + iconv: { + convert: (input, from, to) => { + calls.push({ method: "convert", args: [input, from, to] }); + return (config?.onConvert ?? defaultConvert)(input, from, to); + }, + convertBuffer: (buffer, from, to) => { + calls.push({ method: "convertBuffer", args: [buffer, from, to] }); + return (config?.onConvert ?? defaultConvert)(buffer, from, to); + }, + decode: (buffer, encoding) => { + calls.push({ method: "decode", args: [buffer, encoding] }); + return (config?.onDecode ?? ((b) => decoder.decode(b)))(buffer, encoding); + }, + encode: (str, encoding) => { + calls.push({ method: "encode", args: [str, encoding] }); + if (config?.onEncode) return config.onEncode(str, encoding); + return encoding === "UTF8" || encoding === "UTF-8" ? str : encoder.encode(str); + }, + encodings: () => { + calls.push({ method: "encodings", args: [] }); + return (config?.onEncodings ?? (() => ["UTF-8", "Shift_JIS", "EUC-JP", "ISO-2022-JP"]))(); + }, + Iconv: class { + constructor( + private fromEncoding: string, + private toEncoding: string, + ) {} + convert(input: string | Uint8Array | ArrayBuffer): string | Uint8Array { + calls.push({ method: "convert", args: [input, this.fromEncoding, this.toEncoding] }); + return (config?.onConvert ?? defaultConvert)(input, this.fromEncoding, this.toEncoding); + } + }, + }, + }; + + return { calls }; +} + /** * Creates a function that imports a bundled JS file and returns its `main` export. * Used to test bundled output from `apply --buildOnly`. diff --git a/packages/sdk/tsdown.config.ts b/packages/sdk/tsdown.config.ts index 38efa0f6e..68e959252 100644 --- a/packages/sdk/tsdown.config.ts +++ b/packages/sdk/tsdown.config.ts @@ -21,6 +21,7 @@ export default defineConfig({ "src/cli/skills.ts", "src/utils/test/index.ts", "src/kysely/index.ts", + "src/iconv/index.ts", "src/plugin/index.ts", "src/plugin/builtin/kysely-type/index.ts", "src/plugin/builtin/enum-constants/index.ts", diff --git a/skills/tailor-sdk/SKILL.md b/skills/tailor-sdk/SKILL.md index ee1852d2a..be9346d49 100644 --- a/skills/tailor-sdk/SKILL.md +++ b/skills/tailor-sdk/SKILL.md @@ -17,6 +17,7 @@ Use these files as the single source of truth: - `node_modules/@tailor-platform/sdk/docs/cli-reference.md` - `node_modules/@tailor-platform/sdk/docs/cli/*.md` - `node_modules/@tailor-platform/sdk/docs/testing.md` +- `node_modules/@tailor-platform/sdk/docs/iconv.md` ## Working Rules @@ -30,5 +31,6 @@ Use these files as the single source of truth: - Setup and first deploy: `docs/quickstart.md` - Core config shape: `docs/configuration.md` - Service details: `docs/services/*.md` +- Runtime utilities (iconv etc.): `docs/iconv.md` - CLI commands: `docs/cli-reference.md` and `docs/cli/*.md` - Testing patterns: `docs/testing.md`