spectrum-ts 0.4.0 → 0.6.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.
@@ -1,165 +0,0 @@
1
- // src/content/attachment.ts
2
- import { createReadStream } from "fs";
3
- import { readFile, stat } from "fs/promises";
4
- import { basename } from "path";
5
- import { Readable } from "stream";
6
- import { lookup as lookupMimeType } from "mime-types";
7
- import z from "zod";
8
- var DEFAULT_ATTACHMENT_NAME = "attachment";
9
- var readSchema = z.function({
10
- input: [],
11
- output: z.promise(z.instanceof(Buffer))
12
- });
13
- var streamSchema = z.function({
14
- input: [],
15
- output: z.promise(z.instanceof(ReadableStream))
16
- });
17
- var attachmentSchema = z.object({
18
- type: z.literal("attachment"),
19
- name: z.string().nonempty(),
20
- mimeType: z.string().nonempty(),
21
- size: z.number().int().nonnegative().optional(),
22
- read: readSchema,
23
- stream: streamSchema
24
- });
25
- var resolveAttachmentName = (input, name) => name || (typeof input === "string" ? basename(input) : DEFAULT_ATTACHMENT_NAME);
26
- var resolveAttachmentMimeType = (name, mimeType) => {
27
- if (mimeType) {
28
- return mimeType;
29
- }
30
- const resolvedMimeType = lookupMimeType(name);
31
- if (!resolvedMimeType) {
32
- throw new Error(
33
- `Unable to resolve MIME type for attachment "${name}". Pass options.mimeType explicitly.`
34
- );
35
- }
36
- return resolvedMimeType;
37
- };
38
- var bufferToStream = (buf) => new ReadableStream({
39
- start(controller) {
40
- controller.enqueue(buf);
41
- controller.close();
42
- }
43
- });
44
- var asAttachment = (input) => {
45
- let cached;
46
- const read = () => {
47
- cached ??= input.read().catch((err) => {
48
- cached = void 0;
49
- throw err;
50
- });
51
- return cached;
52
- };
53
- const stream2 = input.stream ?? (async () => bufferToStream(await read()));
54
- return attachmentSchema.parse({
55
- type: "attachment",
56
- name: input.name,
57
- mimeType: input.mimeType,
58
- size: input.size,
59
- read,
60
- stream: stream2
61
- });
62
- };
63
- function attachment(input, options) {
64
- return {
65
- build: async () => {
66
- const name = resolveAttachmentName(input, options?.name);
67
- const mimeType = resolveAttachmentMimeType(name, options?.mimeType);
68
- if (typeof input === "string") {
69
- const stats = await stat(input);
70
- return asAttachment({
71
- name,
72
- mimeType,
73
- size: stats.size,
74
- read: () => readFile(input),
75
- stream: async () => Readable.toWeb(
76
- createReadStream(input)
77
- )
78
- });
79
- }
80
- return asAttachment({
81
- name,
82
- mimeType,
83
- size: input.byteLength,
84
- read: async () => input,
85
- stream: async () => bufferToStream(input)
86
- });
87
- }
88
- };
89
- }
90
-
91
- // src/content/custom.ts
92
- import z2 from "zod";
93
- var customSchema = z2.object({
94
- type: z2.literal("custom"),
95
- raw: z2.unknown()
96
- });
97
- var asCustom = (raw) => customSchema.parse({ type: "custom", raw });
98
- function custom(raw) {
99
- return {
100
- build: async () => asCustom(raw)
101
- };
102
- }
103
-
104
- // src/utils/stream.ts
105
- import { Repeater } from "@repeaterjs/repeater";
106
- function stream(setup) {
107
- const repeater = new Repeater(async (push, stop) => {
108
- const emit = (value) => {
109
- Promise.resolve(push(value)).catch((error) => {
110
- stop(error);
111
- return void 0;
112
- });
113
- };
114
- const end = (error) => {
115
- stop(error);
116
- };
117
- const cleanup = await setup(emit, end);
118
- try {
119
- await stop;
120
- } finally {
121
- await cleanup?.();
122
- }
123
- });
124
- return Object.assign(repeater, {
125
- close: async () => {
126
- await repeater.return(void 0);
127
- }
128
- });
129
- }
130
- function mergeStreams(streams) {
131
- return stream((emit, end) => {
132
- if (streams.length === 0) {
133
- end();
134
- return;
135
- }
136
- let openStreams = streams.length;
137
- const workers = streams.map(async (source) => {
138
- try {
139
- for await (const value of source) {
140
- emit(value);
141
- }
142
- } catch (error) {
143
- end(error);
144
- } finally {
145
- openStreams -= 1;
146
- if (openStreams === 0) {
147
- end();
148
- }
149
- }
150
- });
151
- return async () => {
152
- await Promise.allSettled(streams.map((source) => source.close()));
153
- await Promise.allSettled(workers);
154
- };
155
- });
156
- }
157
-
158
- export {
159
- asAttachment,
160
- attachment,
161
- asCustom,
162
- custom,
163
- stream,
164
- mergeStreams
165
- };
@@ -1,199 +0,0 @@
1
- // src/content/text.ts
2
- import z from "zod";
3
- var textSchema = z.object({
4
- type: z.literal("text"),
5
- text: z.string().nonempty()
6
- });
7
- var asText = (text2) => textSchema.parse({ type: "text", text: text2 });
8
- function text(text2) {
9
- return {
10
- build: async () => asText(text2)
11
- };
12
- }
13
-
14
- // src/content/resolve.ts
15
- var resolveContents = (items) => Promise.all(
16
- items.map((c) => typeof c === "string" ? text(c).build() : c.build())
17
- );
18
-
19
- // src/platform/define.ts
20
- function createPlatformInstance(def, runtime) {
21
- const isPlatformUser = (value) => {
22
- return typeof value === "object" && value !== null && "__platform" in value && value.__platform === def.name;
23
- };
24
- const normalizeSpaceArgs = (args) => {
25
- if (args.length === 0) {
26
- return { users: [], params: void 0 };
27
- }
28
- const [first, ...rest] = args;
29
- if (Array.isArray(first)) {
30
- return {
31
- users: first,
32
- params: rest[0]
33
- };
34
- }
35
- if (!isPlatformUser(first)) {
36
- return {
37
- users: [],
38
- params: first
39
- };
40
- }
41
- const last = args.at(-1);
42
- if (last !== void 0 && !isPlatformUser(last)) {
43
- return {
44
- users: args.slice(0, -1),
45
- params: last
46
- };
47
- }
48
- return {
49
- users: args,
50
- params: void 0
51
- };
52
- };
53
- const base = {
54
- async user(userID) {
55
- const resolved = await def.user.resolve({
56
- input: { userID },
57
- client: runtime.client,
58
- config: runtime.config
59
- });
60
- return {
61
- ...resolved,
62
- __platform: def.name
63
- };
64
- },
65
- async space(...args) {
66
- const { users, params } = normalizeSpaceArgs(args);
67
- let parsedParams = params;
68
- if (params !== void 0 && def.space.params) {
69
- parsedParams = def.space.params.parse(params);
70
- }
71
- const resolved = await def.space.resolve({
72
- input: { users, params: parsedParams },
73
- client: runtime.client,
74
- config: runtime.config
75
- });
76
- const parsedSpace = def.space.schema ? def.space.schema.parse(resolved) : resolved;
77
- const spaceRef = {
78
- id: parsedSpace.id,
79
- __platform: def.name
80
- };
81
- const typingCtx = {
82
- space: spaceRef,
83
- client: runtime.client,
84
- config: runtime.config
85
- };
86
- return {
87
- ...parsedSpace,
88
- ...spaceRef,
89
- send: async (...content) => {
90
- const built = await resolveContents(content);
91
- for (const item of built) {
92
- await def.actions.send({
93
- ...typingCtx,
94
- content: item
95
- });
96
- }
97
- },
98
- startTyping: async () => {
99
- await def.actions.startTyping?.(typingCtx);
100
- },
101
- stopTyping: async () => {
102
- await def.actions.stopTyping?.(typingCtx);
103
- },
104
- responding: async (fn) => {
105
- await def.actions.startTyping?.(typingCtx);
106
- try {
107
- return await fn();
108
- } finally {
109
- await def.actions.stopTyping?.(typingCtx).catch(() => {
110
- });
111
- }
112
- }
113
- };
114
- }
115
- };
116
- const eventProperties = {};
117
- for (const eventName of Object.keys(def.events)) {
118
- if (eventName === "messages") {
119
- continue;
120
- }
121
- const producer = def.events[eventName];
122
- if (producer) {
123
- eventProperties[eventName] = producer({
124
- client: runtime.client,
125
- config: runtime.config
126
- });
127
- }
128
- }
129
- return Object.assign(base, eventProperties);
130
- }
131
- function definePlatform(name, def) {
132
- const fullDef = { name, ...def };
133
- const platformCache = /* @__PURE__ */ new WeakMap();
134
- const narrowSpectrum = (spectrum) => {
135
- const cached = platformCache.get(spectrum);
136
- if (cached) {
137
- return cached;
138
- }
139
- const runtime = spectrum.__internal.platforms.get(name);
140
- if (!runtime) {
141
- throw new Error(`Platform "${name}" is not registered`);
142
- }
143
- const instance = createPlatformInstance(
144
- fullDef,
145
- runtime
146
- );
147
- platformCache.set(spectrum, instance);
148
- return instance;
149
- };
150
- const narrowSpace = (input) => {
151
- if (input.__platform !== name) {
152
- throw new Error(
153
- `Expected space from "${name}", got "${input.__platform}"`
154
- );
155
- }
156
- return input;
157
- };
158
- const narrowMessage = (input) => {
159
- if (input.platform !== name) {
160
- throw new Error(
161
- `Expected message from "${name}", got "${input.platform}"`
162
- );
163
- }
164
- return input;
165
- };
166
- const narrower = ((input) => {
167
- if ("__providers" in input && "__internal" in input) {
168
- return narrowSpectrum(input);
169
- }
170
- if ("__platform" in input && "send" in input) {
171
- return narrowSpace(input);
172
- }
173
- if ("platform" in input && "sender" in input && "space" in input) {
174
- return narrowMessage(input);
175
- }
176
- throw new Error("Invalid input to platform narrowing function");
177
- });
178
- narrower.config = (config) => {
179
- const resolvedConfig = config ?? {};
180
- return {
181
- __tag: "PlatformProviderConfig",
182
- __def: void 0,
183
- __name: name,
184
- config: resolvedConfig,
185
- __definition: fullDef
186
- };
187
- };
188
- if (def.static) {
189
- Object.assign(narrower, def.static);
190
- }
191
- return narrower;
192
- }
193
-
194
- export {
195
- asText,
196
- text,
197
- resolveContents,
198
- definePlatform
199
- };