spectrum-ts 0.9.0 → 1.0.1
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/chunk-2Y5GBI6W.js +129 -0
- package/dist/chunk-7Q7KJKGL.js +117 -0
- package/dist/{chunk-6ZOLTQDN.js → chunk-LAGNM6I7.js} +39 -11
- package/dist/chunk-XMAI2AAN.js +1221 -0
- package/dist/index.d.ts +43 -7
- package/dist/index.js +49 -155
- package/dist/providers/imessage/index.d.ts +16 -7
- package/dist/providers/imessage/index.js +345 -60
- package/dist/providers/terminal/index.d.ts +109 -9
- package/dist/providers/terminal/index.js +813 -32
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +15 -10
- package/dist/{types-B8g0pvfg.d.ts → types-D5KhSXLy.d.ts} +27 -2
- package/package.json +1 -1
- package/dist/chunk-CZIWNTXP.js +0 -710
- package/dist/chunk-PLJI5FTO.js +0 -513
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// src/utils/stream.ts
|
|
2
|
+
import { Repeater } from "@repeaterjs/repeater";
|
|
3
|
+
function stream(setup) {
|
|
4
|
+
const repeater = new Repeater(async (push, stop) => {
|
|
5
|
+
const emit = async (value) => {
|
|
6
|
+
try {
|
|
7
|
+
await push(value);
|
|
8
|
+
} catch (error) {
|
|
9
|
+
stop(error);
|
|
10
|
+
throw error;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const end = (error) => {
|
|
14
|
+
stop(error);
|
|
15
|
+
};
|
|
16
|
+
const cleanup = await setup(emit, end);
|
|
17
|
+
try {
|
|
18
|
+
await stop;
|
|
19
|
+
} finally {
|
|
20
|
+
await cleanup?.();
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return Object.assign(repeater, {
|
|
24
|
+
close: async () => {
|
|
25
|
+
await repeater.return(void 0);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function mergeStreams(streams) {
|
|
30
|
+
return stream((emit, end) => {
|
|
31
|
+
if (streams.length === 0) {
|
|
32
|
+
end();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let openStreams = streams.length;
|
|
36
|
+
const workers = streams.map(async (source) => {
|
|
37
|
+
try {
|
|
38
|
+
for await (const value of source) {
|
|
39
|
+
await emit(value);
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
end(error);
|
|
43
|
+
} finally {
|
|
44
|
+
openStreams -= 1;
|
|
45
|
+
if (openStreams === 0) {
|
|
46
|
+
end();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return async () => {
|
|
51
|
+
await Promise.allSettled(streams.map((source) => source.close()));
|
|
52
|
+
await Promise.allSettled(workers);
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/utils/cloud.ts
|
|
58
|
+
var SPECTRUM_CLOUD_URL = `https://${process.env.SPECTRUM_CLOUD_URL ?? "spectrum.photon.codes"}`;
|
|
59
|
+
var SpectrumCloudError = class extends Error {
|
|
60
|
+
status;
|
|
61
|
+
code;
|
|
62
|
+
constructor(status, code, message) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = "SpectrumCloudError";
|
|
65
|
+
this.status = status;
|
|
66
|
+
this.code = code;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var request = async (path, init) => {
|
|
70
|
+
const response = await fetch(`${SPECTRUM_CLOUD_URL}${path}`, init);
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
const body = await response.text().catch(() => "");
|
|
73
|
+
try {
|
|
74
|
+
const parsed = JSON.parse(body);
|
|
75
|
+
throw new SpectrumCloudError(
|
|
76
|
+
response.status,
|
|
77
|
+
parsed.code,
|
|
78
|
+
parsed.message
|
|
79
|
+
);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (error instanceof SpectrumCloudError) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
throw new SpectrumCloudError(
|
|
85
|
+
response.status,
|
|
86
|
+
"UNKNOWN",
|
|
87
|
+
body || response.statusText
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const json = await response.json();
|
|
92
|
+
if (!json.succeed) {
|
|
93
|
+
throw new SpectrumCloudError(
|
|
94
|
+
response.status,
|
|
95
|
+
"UNKNOWN",
|
|
96
|
+
"Server returned succeed=false"
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return json.data;
|
|
100
|
+
};
|
|
101
|
+
var basicAuth = (projectId, projectSecret) => `Basic ${btoa(`${projectId}:${projectSecret}`)}`;
|
|
102
|
+
var cloud = {
|
|
103
|
+
getSubscription: (projectId) => request(`/projects/${projectId}/billing/subscription`),
|
|
104
|
+
issueImessageTokens: (projectId, projectSecret) => request(`/projects/${projectId}/imessage/tokens`, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: { Authorization: basicAuth(projectId, projectSecret) }
|
|
107
|
+
}),
|
|
108
|
+
getImessageInfo: (projectId) => request(`/projects/${projectId}/imessage/`),
|
|
109
|
+
issueWhatsappBusinessTokens: (projectId, projectSecret) => request(`/projects/${projectId}/whatsapp-business/tokens`, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: { Authorization: basicAuth(projectId, projectSecret) }
|
|
112
|
+
}),
|
|
113
|
+
getPlatforms: (projectId) => request(`/projects/${projectId}/platforms/`),
|
|
114
|
+
togglePlatform: (projectId, projectSecret, platform, enabled) => request(`/projects/${projectId}/platforms/`, {
|
|
115
|
+
method: "PATCH",
|
|
116
|
+
headers: {
|
|
117
|
+
Authorization: basicAuth(projectId, projectSecret),
|
|
118
|
+
"Content-Type": "application/json"
|
|
119
|
+
},
|
|
120
|
+
body: JSON.stringify({ platform, enabled })
|
|
121
|
+
})
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export {
|
|
125
|
+
stream,
|
|
126
|
+
mergeStreams,
|
|
127
|
+
SpectrumCloudError,
|
|
128
|
+
cloud
|
|
129
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bufferToStream,
|
|
3
|
+
readSchema,
|
|
4
|
+
streamSchema
|
|
5
|
+
} from "./chunk-XMAI2AAN.js";
|
|
6
|
+
|
|
7
|
+
// src/content/voice.ts
|
|
8
|
+
import { createReadStream } from "fs";
|
|
9
|
+
import { readFile, stat } from "fs/promises";
|
|
10
|
+
import { basename } from "path";
|
|
11
|
+
import { Readable } from "stream";
|
|
12
|
+
import { lookup as lookupMimeType } from "mime-types";
|
|
13
|
+
import z from "zod";
|
|
14
|
+
var AUDIO_MIME_PATTERN = /^audio\//i;
|
|
15
|
+
var audioMimeSchema = z.string().nonempty().regex(AUDIO_MIME_PATTERN, "voice content requires an audio/* MIME type");
|
|
16
|
+
var voiceSchema = z.object({
|
|
17
|
+
type: z.literal("voice"),
|
|
18
|
+
name: z.string().nonempty().optional(),
|
|
19
|
+
mimeType: audioMimeSchema,
|
|
20
|
+
duration: z.number().nonnegative().optional(),
|
|
21
|
+
size: z.number().int().nonnegative().optional(),
|
|
22
|
+
read: readSchema,
|
|
23
|
+
stream: streamSchema
|
|
24
|
+
});
|
|
25
|
+
var resolveVoiceName = (input, name) => {
|
|
26
|
+
if (name) {
|
|
27
|
+
return name;
|
|
28
|
+
}
|
|
29
|
+
if (typeof input === "string") {
|
|
30
|
+
return basename(input);
|
|
31
|
+
}
|
|
32
|
+
return void 0;
|
|
33
|
+
};
|
|
34
|
+
var resolveVoiceMimeType = (name, mimeType) => {
|
|
35
|
+
if (mimeType) {
|
|
36
|
+
if (!AUDIO_MIME_PATTERN.test(mimeType)) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`voice content requires an audio/* MIME type, got "${mimeType}".`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return mimeType;
|
|
42
|
+
}
|
|
43
|
+
if (name) {
|
|
44
|
+
const resolved = lookupMimeType(name);
|
|
45
|
+
if (resolved && AUDIO_MIME_PATTERN.test(resolved)) {
|
|
46
|
+
return resolved;
|
|
47
|
+
}
|
|
48
|
+
if (resolved) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Resolved non-audio MIME type "${resolved}" from name "${name}". Pass options.mimeType explicitly with an audio/* type.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw new Error(
|
|
55
|
+
"Unable to resolve MIME type for voice content. Pass options.mimeType explicitly."
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
var asVoice = (input) => {
|
|
59
|
+
let cached;
|
|
60
|
+
const read = () => {
|
|
61
|
+
cached ??= input.read().catch((err) => {
|
|
62
|
+
cached = void 0;
|
|
63
|
+
throw err;
|
|
64
|
+
});
|
|
65
|
+
return cached;
|
|
66
|
+
};
|
|
67
|
+
const stream = input.stream ?? (async () => bufferToStream(await read()));
|
|
68
|
+
return voiceSchema.parse({
|
|
69
|
+
type: "voice",
|
|
70
|
+
name: input.name,
|
|
71
|
+
mimeType: input.mimeType,
|
|
72
|
+
duration: input.duration,
|
|
73
|
+
size: input.size,
|
|
74
|
+
read,
|
|
75
|
+
stream
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
function voice(input, options) {
|
|
79
|
+
return {
|
|
80
|
+
build: async () => {
|
|
81
|
+
const name = resolveVoiceName(input, options?.name);
|
|
82
|
+
const mimeHint = typeof input === "string" ? basename(input) : name;
|
|
83
|
+
const mimeType = resolveVoiceMimeType(mimeHint, options?.mimeType);
|
|
84
|
+
if (typeof input === "string") {
|
|
85
|
+
const stats = await stat(input);
|
|
86
|
+
if (!stats.isFile()) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`voice content path "${input}" is not a regular file.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return asVoice({
|
|
92
|
+
name,
|
|
93
|
+
mimeType,
|
|
94
|
+
duration: options?.duration,
|
|
95
|
+
size: stats.size,
|
|
96
|
+
read: () => readFile(input),
|
|
97
|
+
stream: async () => Readable.toWeb(
|
|
98
|
+
createReadStream(input)
|
|
99
|
+
)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return asVoice({
|
|
103
|
+
name,
|
|
104
|
+
mimeType,
|
|
105
|
+
duration: options?.duration,
|
|
106
|
+
size: input.byteLength,
|
|
107
|
+
read: async () => input,
|
|
108
|
+
stream: async () => bufferToStream(input)
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
asVoice,
|
|
116
|
+
voice
|
|
117
|
+
};
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bufferToStream,
|
|
3
3
|
readSchema,
|
|
4
|
+
resolveContents,
|
|
4
5
|
streamSchema
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XMAI2AAN.js";
|
|
6
7
|
|
|
7
|
-
// src/content/
|
|
8
|
+
// src/content/group.ts
|
|
8
9
|
import z from "zod";
|
|
10
|
+
var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
11
|
+
var groupSchema = z.object({
|
|
12
|
+
type: z.literal("group"),
|
|
13
|
+
items: z.array(z.custom(isMessage)).min(2)
|
|
14
|
+
});
|
|
15
|
+
var asGroup = (input) => groupSchema.parse({ type: "group", items: input.items });
|
|
16
|
+
var stubOutboundMessage = (content) => ({ id: "", content });
|
|
17
|
+
function group(...items) {
|
|
18
|
+
return {
|
|
19
|
+
build: async () => {
|
|
20
|
+
const resolved = await resolveContents(items);
|
|
21
|
+
const members = [];
|
|
22
|
+
for (const item of resolved) {
|
|
23
|
+
if (item.type === "group" || item.type === "reaction") {
|
|
24
|
+
throw new Error(`group() cannot contain "${item.type}" items`);
|
|
25
|
+
}
|
|
26
|
+
members.push(stubOutboundMessage(item));
|
|
27
|
+
}
|
|
28
|
+
return asGroup({ items: members });
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/content/richlink.ts
|
|
34
|
+
import z2 from "zod";
|
|
9
35
|
|
|
10
36
|
// src/utils/link-metadata.ts
|
|
11
37
|
import ogs from "open-graph-scraper";
|
|
@@ -76,22 +102,22 @@ var fetchImage = async (url) => {
|
|
|
76
102
|
};
|
|
77
103
|
|
|
78
104
|
// src/content/richlink.ts
|
|
79
|
-
var richlinkCoverSchema =
|
|
80
|
-
mimeType:
|
|
105
|
+
var richlinkCoverSchema = z2.object({
|
|
106
|
+
mimeType: z2.string().min(1).optional(),
|
|
81
107
|
read: readSchema,
|
|
82
108
|
stream: streamSchema
|
|
83
109
|
});
|
|
84
|
-
var optionalStringAccessor =
|
|
110
|
+
var optionalStringAccessor = z2.function({
|
|
85
111
|
input: [],
|
|
86
|
-
output:
|
|
112
|
+
output: z2.promise(z2.string().min(1).optional())
|
|
87
113
|
});
|
|
88
|
-
var coverAccessor =
|
|
114
|
+
var coverAccessor = z2.function({
|
|
89
115
|
input: [],
|
|
90
|
-
output:
|
|
116
|
+
output: z2.promise(richlinkCoverSchema.optional())
|
|
91
117
|
});
|
|
92
|
-
var richlinkSchema =
|
|
93
|
-
type:
|
|
94
|
-
url:
|
|
118
|
+
var richlinkSchema = z2.object({
|
|
119
|
+
type: z2.literal("richlink"),
|
|
120
|
+
url: z2.url(),
|
|
95
121
|
title: optionalStringAccessor,
|
|
96
122
|
summary: optionalStringAccessor,
|
|
97
123
|
cover: coverAccessor
|
|
@@ -136,6 +162,8 @@ function richlink(url) {
|
|
|
136
162
|
}
|
|
137
163
|
|
|
138
164
|
export {
|
|
165
|
+
asGroup,
|
|
166
|
+
group,
|
|
139
167
|
asRichlink,
|
|
140
168
|
richlink
|
|
141
169
|
};
|