@ryusenkei/maezae 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/delivery.js +119 -8
- package/dist/dev.d.ts +1 -0
- package/dist/dev.js +100 -0
- package/dist/types/cms-contract/src/errors.d.ts +1 -0
- package/dist/types/delivery-client/src/index.d.ts +2 -1
- package/dist/types/delivery-client/src/media.d.ts +5 -3
- package/dist/types/delivery-client/src/values.d.ts +2 -1
- package/dist/types/model/src/index.d.ts +1 -0
- package/dist/types/model/src/mediaUrl.d.ts +41 -0
- package/dist/types/sdk/src/delivery.d.ts +2 -2
- package/dist/types/sdk/src/dev.d.ts +22 -0
- package/package.json +6 -2
package/dist/delivery.js
CHANGED
|
@@ -116,18 +116,129 @@ function createDeliveryClient(delivery, { drafts = false, at } = {}) {
|
|
|
116
116
|
prefetch
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
+
// ../model/src/mediaUrl.ts
|
|
120
|
+
/** 配信する形式と拡張子 */
|
|
121
|
+
const EXTENSIONS = {
|
|
122
|
+
"image/jpeg": "jpg",
|
|
123
|
+
"image/png": "png",
|
|
124
|
+
"image/webp": "webp",
|
|
125
|
+
"image/gif": "gif",
|
|
126
|
+
"video/mp4": "mp4",
|
|
127
|
+
"video/webm": "webm"
|
|
128
|
+
};
|
|
129
|
+
/** 拡張子の形式(配信しない拡張子は null) */
|
|
130
|
+
const mediaMimeOf = (ext) => Object.entries(EXTENSIONS).find(([, e]) => e === ext)?.[0] ?? null;
|
|
131
|
+
const IMAGE_PATH = /^\/images\/([A-Za-z0-9_-]+)\/([A-Za-z0-9_-]+)-(\d+)x(\d+)\.([a-z0-9]+)$/;
|
|
132
|
+
const VIDEO_PATH = /^\/videos\/([A-Za-z0-9_-]+)\/([A-Za-z0-9_-]+)\.([a-z0-9]+)$/;
|
|
133
|
+
/** 配信 URL のパスを解析(形が違えば null) */
|
|
134
|
+
function parseMediaPath(pathname) {
|
|
135
|
+
const image = IMAGE_PATH.exec(pathname);
|
|
136
|
+
if (image) {
|
|
137
|
+
const [, tenant, id, width, height, ext] = image;
|
|
138
|
+
return mediaMimeOf(ext)?.startsWith("image/") ? {
|
|
139
|
+
kind: "image",
|
|
140
|
+
tenant,
|
|
141
|
+
id,
|
|
142
|
+
ext,
|
|
143
|
+
width: Number(width),
|
|
144
|
+
height: Number(height)
|
|
145
|
+
} : null;
|
|
146
|
+
}
|
|
147
|
+
const video = VIDEO_PATH.exec(pathname);
|
|
148
|
+
if (video) {
|
|
149
|
+
const [, tenant, id, ext] = video;
|
|
150
|
+
return mediaMimeOf(ext)?.startsWith("video/") ? {
|
|
151
|
+
kind: "video",
|
|
152
|
+
tenant,
|
|
153
|
+
id,
|
|
154
|
+
ext
|
|
155
|
+
} : null;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
/** 幅・高さの上限(画素) */
|
|
160
|
+
const IMAGE_SIZE_MAX = 4096;
|
|
161
|
+
const FITS = [
|
|
162
|
+
"scale-down",
|
|
163
|
+
"contain",
|
|
164
|
+
"cover",
|
|
165
|
+
"crop",
|
|
166
|
+
"pad"
|
|
167
|
+
];
|
|
168
|
+
const FORMATS = [
|
|
169
|
+
"jpg",
|
|
170
|
+
"png",
|
|
171
|
+
"webp",
|
|
172
|
+
"avif"
|
|
173
|
+
];
|
|
174
|
+
/** URL に画像の変換のクエリを設定(ほかのクエリは残す) */
|
|
175
|
+
function withImageParams(url, params) {
|
|
176
|
+
const u = new URL(url);
|
|
177
|
+
const { rect, ...rest } = params;
|
|
178
|
+
for (const [k, v] of Object.entries(rest)) if (v !== void 0) u.searchParams.set(k, String(v));
|
|
179
|
+
if (rect) u.searchParams.set("rect", [
|
|
180
|
+
rect.left,
|
|
181
|
+
rect.top,
|
|
182
|
+
rect.width,
|
|
183
|
+
rect.height
|
|
184
|
+
].map(Math.round).join(","));
|
|
185
|
+
return u.href;
|
|
186
|
+
}
|
|
187
|
+
const intIn = (v, min, max) => {
|
|
188
|
+
if (v === null) return void 0;
|
|
189
|
+
if (!/^\d+$/.test(v)) return null;
|
|
190
|
+
const n = Number(v);
|
|
191
|
+
return n >= min && n <= max ? n : null;
|
|
192
|
+
};
|
|
193
|
+
/** クエリから画像の変換(不正な値があれば null、変換なしは空のオブジェクト) */
|
|
194
|
+
function imageParamsOf(query) {
|
|
195
|
+
const w = intIn(query.get("w"), 1, IMAGE_SIZE_MAX);
|
|
196
|
+
const h = intIn(query.get("h"), 1, IMAGE_SIZE_MAX);
|
|
197
|
+
const q = intIn(query.get("q"), 1, 100);
|
|
198
|
+
if (w === null || h === null || q === null) return null;
|
|
199
|
+
const fit = query.get("fit");
|
|
200
|
+
const fm = query.get("fm");
|
|
201
|
+
const auto = query.get("auto");
|
|
202
|
+
if (fit !== null && !FITS.includes(fit) || fm !== null && !FORMATS.includes(fm) || auto !== null && auto !== "format") return null;
|
|
203
|
+
const rectValue = query.get("rect");
|
|
204
|
+
let rect;
|
|
205
|
+
if (rectValue !== null) {
|
|
206
|
+
const parts = rectValue.split(",");
|
|
207
|
+
if (parts.length !== 4 || !parts.every((p) => /^\d+$/.test(p))) return null;
|
|
208
|
+
const [left, top, width, height] = parts.map(Number);
|
|
209
|
+
if (width < 1 || height < 1) return null;
|
|
210
|
+
rect = {
|
|
211
|
+
left,
|
|
212
|
+
top,
|
|
213
|
+
width,
|
|
214
|
+
height
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
...w !== void 0 && { w },
|
|
219
|
+
...h !== void 0 && { h },
|
|
220
|
+
...q !== void 0 && { q },
|
|
221
|
+
...fit !== null && { fit },
|
|
222
|
+
...fm !== null && { fm },
|
|
223
|
+
...auto !== null && { auto: "format" },
|
|
224
|
+
...rect && { rect }
|
|
225
|
+
};
|
|
226
|
+
}
|
|
119
227
|
// ../delivery-client/src/media.ts
|
|
228
|
+
/** 配信 URL の画像の切り抜き(クエリ rect、範囲なしは元の URL) */
|
|
229
|
+
const deliveredImageUrl = (src, rect) => rect ? withImageParams(src, { rect }) : src;
|
|
120
230
|
/**
|
|
121
231
|
* 実体から表示の素材への変換(動画は代わりの画像も)
|
|
122
|
-
* -
|
|
123
|
-
* - 画像の変換: imageUrl
|
|
124
|
-
* - imageUrl
|
|
232
|
+
* - URL: 実体の url、なしは base/<ID>(base は実体の URL の前半)
|
|
233
|
+
* - 画像の変換: imageUrl で画像(動画の実体は除く)を変換の URL に、url のある実体の既定は deliveredImageUrl、切り抜きは切り抜いた画像の URL・寸法・見せたい点
|
|
234
|
+
* - url も imageUrl もない: 元の画像のまま(手元の開発)
|
|
125
235
|
*/
|
|
126
236
|
function mediaOf(base, imageUrl) {
|
|
127
237
|
const still = (a) => {
|
|
128
|
-
const src = `${base}/${a.id}`;
|
|
238
|
+
const src = a.url ?? `${base}/${a.id}`;
|
|
239
|
+
const transform = imageUrl ?? (a.url ? deliveredImageUrl : void 0);
|
|
129
240
|
const alt = a.alt ?? "";
|
|
130
|
-
if (!
|
|
241
|
+
if (!transform || a.kind !== "image") return {
|
|
131
242
|
src,
|
|
132
243
|
width: a.width,
|
|
133
244
|
height: a.height,
|
|
@@ -135,7 +246,7 @@ function mediaOf(base, imageUrl) {
|
|
|
135
246
|
...a.hotspot ? { hotspot: a.hotspot } : {}
|
|
136
247
|
};
|
|
137
248
|
if (!a.crop) return {
|
|
138
|
-
src:
|
|
249
|
+
src: transform(src),
|
|
139
250
|
width: a.width,
|
|
140
251
|
height: a.height,
|
|
141
252
|
alt,
|
|
@@ -143,7 +254,7 @@ function mediaOf(base, imageUrl) {
|
|
|
143
254
|
};
|
|
144
255
|
const rect = cropRect(a.width, a.height, a.crop);
|
|
145
256
|
return {
|
|
146
|
-
src:
|
|
257
|
+
src: transform(src, rect),
|
|
147
258
|
width: rect.width,
|
|
148
259
|
height: rect.height,
|
|
149
260
|
alt,
|
|
@@ -196,4 +307,4 @@ function createDeliveryClock(timeZone) {
|
|
|
196
307
|
})
|
|
197
308
|
};
|
|
198
309
|
}
|
|
199
|
-
export { DELIVERY_PATH, DeliveryError, createDeliveryClient, createDeliveryClock, fieldOf, limitOf, listParams, mediaOf, optionLabels, pagePath, subPath };
|
|
310
|
+
export { DELIVERY_PATH, DeliveryError, createDeliveryClient, createDeliveryClock, deliveredImageUrl, fieldOf, imageParamsOf, limitOf, listParams, mediaOf, optionLabels, pagePath, parseMediaPath, subPath, withImageParams };
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types/sdk/src/dev.js';
|
package/dist/dev.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { i as SIGNED_HEADERS, s as signedHeaders } from "./src-Baj-EC2G.js";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { createServer } from "node:http";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
// src/dev.ts
|
|
7
|
+
/** 記録する応答のヘッダー(配信のキャッシュの照合と本文の形式) */
|
|
8
|
+
const KEPT_HEADERS = [
|
|
9
|
+
"content-type",
|
|
10
|
+
"etag",
|
|
11
|
+
"x-stale"
|
|
12
|
+
];
|
|
13
|
+
/** 記録のディレクトリの URL(末尾はスラッシュ) */
|
|
14
|
+
const dirUrl = (dir) => {
|
|
15
|
+
const url = dir instanceof URL ? dir : pathToFileURL(dir);
|
|
16
|
+
return url.href.endsWith("/") ? url : new URL(`${url.href}/`);
|
|
17
|
+
};
|
|
18
|
+
/** 呼び出しの記録のファイル */
|
|
19
|
+
const fileOf = (dir, method, path, ifNoneMatch, body) => new URL(`${createHash("sha256").update(`${method} ${path}\n${ifNoneMatch}\n${body}`).digest("hex").slice(0, 16)}.json`, dir);
|
|
20
|
+
/** upstream へ中継(API の鍵を付与、下書きの署名は upstream の URL で改めて付与) */
|
|
21
|
+
async function relay({ origin, apiKey, previewSecret }, path, init) {
|
|
22
|
+
const url = `${origin.replace(/\/$/, "")}${path}`;
|
|
23
|
+
const body = typeof init.body === "string" ? init.body : null;
|
|
24
|
+
const headers = new Headers(init.headers);
|
|
25
|
+
if (previewSecret && headers.has(SIGNED_HEADERS.preview)) headers.set(SIGNED_HEADERS.preview, (await signedHeaders(previewSecret, {
|
|
26
|
+
method: init.method ?? "GET",
|
|
27
|
+
url,
|
|
28
|
+
body
|
|
29
|
+
}, { header: SIGNED_HEADERS.preview })).get(SIGNED_HEADERS.preview));
|
|
30
|
+
headers.set("authorization", `Bearer ${apiKey}`);
|
|
31
|
+
return fetch(url, {
|
|
32
|
+
...init,
|
|
33
|
+
headers
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/** 記録の配信 API(port Delivery、upstream があれば中継して保存、並行の書き込みは一時のファイルから置き換え) */
|
|
37
|
+
function fixtureDelivery({ dir, upstream }) {
|
|
38
|
+
const base = dirUrl(dir);
|
|
39
|
+
return async (path, init = {}) => {
|
|
40
|
+
const method = (init.method ?? "GET").toUpperCase();
|
|
41
|
+
const file = fileOf(base, method, path, new Headers(init.headers).get("if-none-match") ?? "", typeof init.body === "string" ? init.body : "");
|
|
42
|
+
let recorded;
|
|
43
|
+
if (upstream) {
|
|
44
|
+
const res = await relay(upstream, path, init);
|
|
45
|
+
recorded = {
|
|
46
|
+
method,
|
|
47
|
+
path,
|
|
48
|
+
status: res.status,
|
|
49
|
+
headers: Object.fromEntries(KEPT_HEADERS.flatMap((h) => res.headers.has(h) ? [[h, res.headers.get(h)]] : [])),
|
|
50
|
+
body: await res.text()
|
|
51
|
+
};
|
|
52
|
+
mkdirSync(base, { recursive: true });
|
|
53
|
+
const tmp = new URL(`${file.href}.${process.pid}.tmp`);
|
|
54
|
+
writeFileSync(tmp, `${JSON.stringify(recorded, null, 2)}\n`);
|
|
55
|
+
renameSync(tmp, file);
|
|
56
|
+
} else {
|
|
57
|
+
if (!existsSync(file)) throw new Error(`配信 API の記録なし: ${method} ${path}(upstream を指定して記録)`);
|
|
58
|
+
recorded = JSON.parse(readFileSync(file, "utf8"));
|
|
59
|
+
}
|
|
60
|
+
return new Response(recorded.status === 304 ? null : recorded.body, {
|
|
61
|
+
status: recorded.status,
|
|
62
|
+
headers: recorded.headers
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** 手元の配信 API のサーバー(/content/v1/… だけ、記録にない呼び出しは502、戻り値は origin と停止の関数) */
|
|
67
|
+
async function serveDelivery(options) {
|
|
68
|
+
const delivery = fixtureDelivery(options);
|
|
69
|
+
const server = createServer(async (req, res) => {
|
|
70
|
+
const chunks = [];
|
|
71
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
72
|
+
const path = req.url ?? "/";
|
|
73
|
+
if (!path.startsWith("/content/v1")) {
|
|
74
|
+
res.writeHead(404).end();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const headers = Object.entries(req.headers).flatMap(([k, v]) => typeof v === "string" && k !== "host" ? [[k, v]] : []);
|
|
78
|
+
try {
|
|
79
|
+
const response = await delivery(path, {
|
|
80
|
+
method: req.method ?? "GET",
|
|
81
|
+
headers,
|
|
82
|
+
...chunks.length ? { body: Buffer.concat(chunks).toString("utf8") } : {}
|
|
83
|
+
});
|
|
84
|
+
const out = {};
|
|
85
|
+
response.headers.forEach((v, k) => out[k] = v);
|
|
86
|
+
res.writeHead(response.status, out);
|
|
87
|
+
res.end(Buffer.from(await response.arrayBuffer()));
|
|
88
|
+
} catch (e) {
|
|
89
|
+
res.writeHead(502, { "content-type": "text/plain; charset=utf-8" }).end(e instanceof Error ? e.message : String(e));
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const hostname = options.hostname ?? "127.0.0.1";
|
|
93
|
+
await new Promise((resolve) => server.listen(options.port ?? 0, hostname, resolve));
|
|
94
|
+
const address = server.address();
|
|
95
|
+
return {
|
|
96
|
+
origin: `http://${hostname}:${typeof address === "object" && address ? address.port : options.port}`,
|
|
97
|
+
close: () => new Promise((resolve) => server.close(() => resolve()))
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export { fixtureDelivery, serveDelivery };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { createDeliveryClient, DELIVERY_PATH, DeliveryError, subPath, type BundleResult, type Delivery, type DeliveryClient, type Prefetched } from './client.js';
|
|
2
|
-
export { mediaOf, type ImageUrl, type MediaOf } from './media.js';
|
|
2
|
+
export { deliveredImageUrl, mediaOf, type ImageUrl, type MediaOf } from './media.js';
|
|
3
|
+
export { imageParamsOf, parseMediaPath, withImageParams, type ImageParams, type MediaPath } from '../../model/src/index.js';
|
|
3
4
|
export { listParams, type ListFilter, type ListQuery, type Page } from './query.js';
|
|
4
5
|
export { createDeliveryClock, fieldOf, limitOf, optionLabels, pagePath, type DeliveryClock } from './schema.js';
|
|
5
6
|
export type { DeliveredAsset, FieldValue, Item, ItemOf } from './values.js';
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type CropRect, type Media } from '../../model/src/index.js';
|
|
2
2
|
/** 配信する画像の URL(元の URL と、元画像の画素での切り抜きの範囲から、配信の側で変換する URL) */
|
|
3
3
|
export type ImageUrl = (src: string, rect?: CropRect) => string;
|
|
4
|
+
/** 配信 URL の画像の切り抜き(クエリ rect、範囲なしは元の URL) */
|
|
5
|
+
export declare const deliveredImageUrl: ImageUrl;
|
|
4
6
|
/** 実体から表示の素材(見つからない実体はなし) */
|
|
5
7
|
export type MediaOf = (asset: unknown) => Media | undefined;
|
|
6
8
|
/**
|
|
7
9
|
* 実体から表示の素材への変換(動画は代わりの画像も)
|
|
8
|
-
* -
|
|
9
|
-
* - 画像の変換: imageUrl
|
|
10
|
-
* - imageUrl
|
|
10
|
+
* - URL: 実体の url、なしは base/<ID>(base は実体の URL の前半)
|
|
11
|
+
* - 画像の変換: imageUrl で画像(動画の実体は除く)を変換の URL に、url のある実体の既定は deliveredImageUrl、切り抜きは切り抜いた画像の URL・寸法・見せたい点
|
|
12
|
+
* - url も imageUrl もない: 元の画像のまま(手元の開発)
|
|
11
13
|
*/
|
|
12
14
|
export declare function mediaOf(base: string, imageUrl?: ImageUrl): MediaOf;
|
|
@@ -2,10 +2,11 @@ import type { Body } from '../../body/src/index.js';
|
|
|
2
2
|
import type { ContentType, Crop, DateString, FieldDef, Hotspot } from '../../model/src/index.js';
|
|
3
3
|
/** 配信 API の1件の値(項目の key と id・updated_at) */
|
|
4
4
|
export type Item = Readonly<Record<string, unknown>>;
|
|
5
|
-
/** 配信 API が解決した実体(hotspot・crop は元の画像に対する割合) */
|
|
5
|
+
/** 配信 API が解決した実体(url は配信 URL で配信のオリジンがある CMS だけ、hotspot・crop は元の画像に対する割合) */
|
|
6
6
|
export type DeliveredAsset = Readonly<{
|
|
7
7
|
id: string;
|
|
8
8
|
kind: 'image' | 'video';
|
|
9
|
+
url?: string;
|
|
9
10
|
width: number;
|
|
10
11
|
height: number;
|
|
11
12
|
alt: string;
|
|
@@ -2,6 +2,7 @@ export { fieldZod, jsonSchemaOf, normalizeValues, zodOf, type ValueOptions } fro
|
|
|
2
2
|
export { assetRefSchema, cropSchema, hotspotSchema, mediaSchema, type Asset, type AssetRef, type Crop, type Hotspot, type LocalImage, type Media } from './asset.js';
|
|
3
3
|
export { coverPosition, coverStart, cropRect, croppedHotspot, objectPosition, stillOf, type CropRect } from './image.js';
|
|
4
4
|
export { IMAGE_TYPE, VIDEO_TYPE } from './media.js';
|
|
5
|
+
export { IMAGE_SIZE_MAX, imageParamsOf, mediaExtOf, mediaMimeOf, mediaPathOf, parseMediaPath, withImageParams, type ImageParams, type MediaPath } from './mediaUrl.js';
|
|
5
6
|
export { diffSchema, withFixed, type ChangeKind, type ChangeLevel, type DiffOptions, type SchemaChange } from './changes.js';
|
|
6
7
|
export { requiredTypeWithTrait, tableTraitsOf, typeWithTrait, type TableTraits } from './capabilities.js';
|
|
7
8
|
export { appliesTo, checkFields, fieldsFor } from './check.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CropRect } from './image.js';
|
|
2
|
+
/** 形式の拡張子(配信しない形式は null) */
|
|
3
|
+
export declare const mediaExtOf: (mimeType: string) => string | null;
|
|
4
|
+
/** 拡張子の形式(配信しない拡張子は null) */
|
|
5
|
+
export declare const mediaMimeOf: (ext: string) => string | null;
|
|
6
|
+
/** 配信 URL のパスの要素(寸法は画像だけ) */
|
|
7
|
+
export type MediaPath = Readonly<{
|
|
8
|
+
kind: 'image' | 'video';
|
|
9
|
+
tenant: string;
|
|
10
|
+
id: string;
|
|
11
|
+
ext: string;
|
|
12
|
+
width?: number;
|
|
13
|
+
height?: number;
|
|
14
|
+
}>;
|
|
15
|
+
/** 配信 URL のパス(媒体・ID の形が不正、配信しない形式なら null) */
|
|
16
|
+
export declare function mediaPathOf({ kind, tenant, id, mimeType, width, height }: Readonly<{
|
|
17
|
+
kind: 'image' | 'video';
|
|
18
|
+
tenant: string;
|
|
19
|
+
id: string;
|
|
20
|
+
mimeType: string;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}>): string | null;
|
|
24
|
+
/** 配信 URL のパスを解析(形が違えば null) */
|
|
25
|
+
export declare function parseMediaPath(pathname: string): MediaPath | null;
|
|
26
|
+
/** 画像の変換のクエリ(w・h は表示の画素、rect は元の画像の画素での切り抜き、auto=format はブラウザに合わせた形式) */
|
|
27
|
+
export type ImageParams = Readonly<{
|
|
28
|
+
w?: number;
|
|
29
|
+
h?: number;
|
|
30
|
+
fit?: 'scale-down' | 'contain' | 'cover' | 'crop' | 'pad';
|
|
31
|
+
rect?: CropRect;
|
|
32
|
+
q?: number;
|
|
33
|
+
fm?: 'jpg' | 'png' | 'webp' | 'avif';
|
|
34
|
+
auto?: 'format';
|
|
35
|
+
}>;
|
|
36
|
+
/** 幅・高さの上限(画素) */
|
|
37
|
+
export declare const IMAGE_SIZE_MAX = 4096;
|
|
38
|
+
/** URL に画像の変換のクエリを設定(ほかのクエリは残す) */
|
|
39
|
+
export declare function withImageParams(url: string, params: ImageParams): string;
|
|
40
|
+
/** クエリから画像の変換(不正な値があれば null、変換なしは空のオブジェクト) */
|
|
41
|
+
export declare function imageParamsOf(query: URLSearchParams): ImageParams | null;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createDeliveryClient, createDeliveryClock, DELIVERY_PATH, DeliveryError, fieldOf, limitOf, listParams, mediaOf, optionLabels, pagePath, subPath } from '../../delivery-client/src/index.js';
|
|
2
|
-
export type { BundleResult, DeliveredAsset, Delivery, DeliveryClient, DeliveryClock, FieldValue, ImageUrl, Item, ItemOf, ListFilter, ListQuery, MediaOf, Page, Prefetched } from '../../delivery-client/src/index.js';
|
|
1
|
+
export { createDeliveryClient, createDeliveryClock, deliveredImageUrl, DELIVERY_PATH, DeliveryError, fieldOf, imageParamsOf, limitOf, listParams, mediaOf, optionLabels, pagePath, parseMediaPath, subPath, withImageParams } from '../../delivery-client/src/index.js';
|
|
2
|
+
export type { BundleResult, DeliveredAsset, Delivery, DeliveryClient, DeliveryClock, FieldValue, ImageParams, ImageUrl, Item, ItemOf, ListFilter, ListQuery, MediaOf, MediaPath, Page, Prefetched } from '../../delivery-client/src/index.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Delivery } from '../../delivery-client/src/index.js';
|
|
2
|
+
/** 公開の配信 API の取得先(origin は媒体の識別子までの URL、例: <公開の配信 API のオリジン>/demo) */
|
|
3
|
+
export type Upstream = Readonly<{
|
|
4
|
+
origin: string;
|
|
5
|
+
apiKey: string;
|
|
6
|
+
previewSecret?: string;
|
|
7
|
+
}>;
|
|
8
|
+
/** 記録の配信 API の指定(dir は記録のディレクトリ、upstream があれば中継して記録) */
|
|
9
|
+
export type FixtureOptions = Readonly<{
|
|
10
|
+
dir: string | URL;
|
|
11
|
+
upstream?: Upstream;
|
|
12
|
+
}>;
|
|
13
|
+
/** 記録の配信 API(port Delivery、upstream があれば中継して保存、並行の書き込みは一時のファイルから置き換え) */
|
|
14
|
+
export declare function fixtureDelivery({ dir, upstream }: FixtureOptions): Delivery;
|
|
15
|
+
/** 手元の配信 API のサーバー(/content/v1/… だけ、記録にない呼び出しは502、戻り値は origin と停止の関数) */
|
|
16
|
+
export declare function serveDelivery(options: FixtureOptions & Readonly<{
|
|
17
|
+
port?: number;
|
|
18
|
+
hostname?: string;
|
|
19
|
+
}>): Promise<Readonly<{
|
|
20
|
+
origin: string;
|
|
21
|
+
close: () => Promise<void>;
|
|
22
|
+
}>>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryusenkei/maezae",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "maezae の導入先のサイト向けの SDK(配信 API
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "maezae の導入先のサイト向けの SDK(配信 API の取得・本文の型・プレビューの署名・手元の開発の配信 API)",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "RYUSENKEI",
|
|
7
7
|
"repository": {
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"./trace": {
|
|
40
40
|
"types": "./dist/trace.d.ts",
|
|
41
41
|
"default": "./dist/trace.js"
|
|
42
|
+
},
|
|
43
|
+
"./dev": {
|
|
44
|
+
"types": "./dist/dev.d.ts",
|
|
45
|
+
"default": "./dist/dev.js"
|
|
42
46
|
}
|
|
43
47
|
},
|
|
44
48
|
"files": [
|