effect-libreoffice 1.0.0 → 1.0.5

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/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License
2
+
3
+ Copyright 2025 Filip Weiss
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # effect-libreoffice
2
2
 
3
+ [![NPM Version](https://img.shields.io/npm/v/effect-libreoffice)](https://www.npmjs.com/package/effect-libreoffice)
4
+ [![GitHub License](https://img.shields.io/github/license/fiws/effect-libreoffice)](https://github.com/fiws/effect-libreoffice/blob/main/LICENSE)
5
+ [![Effect: yes](https://img.shields.io/badge/effect-yes-blue)](https://effect.website/)
6
+
3
7
  A Effect-based wrapper for LibreOffice, providing multiple strategies for document conversion.
4
8
 
5
9
  ## Implementations
@@ -11,12 +15,12 @@ This library offers two distinct implementations for interacting with LibreOffic
11
15
 
12
16
  ### Comparison
13
17
 
14
- | Feature | LibreOfficeCmd (Default) | UnoClient (Uno) |
15
- | :-------------- | :--------------------------------------- | :----------------------------------------- |
16
- | **Method** | Spawns a new process for each conversion | Connects to a long-running server |
17
- | **Performance** | Slower (~440ms/file) | Fast (~60ms/file) |
18
- | **Setup** | Requires LibreOffice installed locally | Requires `unoserver` |
19
- | **Best For** | CLI tools, low volume, simple setup | Servers, high volume, performance critical |
18
+ | Feature | LibreOfficeCmd (Default) | UnoClient (Uno) |
19
+ | :-------------- | :--------------------------------------- | :--------------------------------------------------------- |
20
+ | **Method** | Spawns a new process for each conversion | Connects to a long-running server |
21
+ | **Performance** | Slower (~440ms/file) | Fast (~60ms/file) |
22
+ | **Setup** | Requires LibreOffice installed locally | Requires [unoserver](https://github.com/unoconv/unoserver) |
23
+ | **Best For** | CLI tools, low volume, simple setup | Servers, high volume, performance critical |
20
24
 
21
25
  ## Usage
22
26
 
@@ -0,0 +1,98 @@
1
+ import { FileSystem, HttpClient, HttpClientResponse, Path } from "@effect/platform";
2
+ import { Context, Effect, Layer, Schema } from "effect";
3
+ import * as _effect_platform_Error0 from "@effect/platform/Error";
4
+ import * as _effect_platform_CommandExecutor0 from "@effect/platform/CommandExecutor";
5
+ import * as effect_Types0 from "effect/Types";
6
+ import * as effect_Cause0 from "effect/Cause";
7
+ import * as effect_Scope0 from "effect/Scope";
8
+ import * as _effect_platform_HttpClientError0 from "@effect/platform/HttpClientError";
9
+ import * as effect_ParseResult0 from "effect/ParseResult";
10
+
11
+ //#region src/shared.d.ts
12
+ type KnownSupportedOutputFormat = "pdf" | "docx" | "doc" | "odt" | "html" | "rtf" | "epub" | "jpg" | "txt";
13
+ type OutputPath = `${string}.${KnownSupportedOutputFormat}` | (string & {});
14
+ type Reason = "InputFileNotFound" | "StartFailed" | "Unknown" | "BadOutputExtension" | "MethodNotFound" | "PermissionDenied";
15
+ declare const LibreOfficeError_base: new <A extends Record<string, any> = {}>(args: effect_Types0.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause0.YieldableError & {
16
+ readonly _tag: "LibreOfficeError";
17
+ } & Readonly<A>;
18
+ declare class LibreOfficeError extends LibreOfficeError_base<{
19
+ reason: Reason;
20
+ message: string;
21
+ cause?: unknown;
22
+ }> {}
23
+ //#endregion
24
+ //#region src/uno/uno.d.ts
25
+ declare const UnoError_base: Schema.TaggedErrorClass<UnoError, "UnoError", {
26
+ readonly _tag: Schema.tag<"UnoError">;
27
+ } & {
28
+ message: typeof Schema.String;
29
+ reason: Schema.Literal<["StartFailed", "Unknown", "InputFileNotFound", "BadOutputExtension", "MethodNotFound", "PermissionDenied"]>;
30
+ cause: Schema.optional<typeof Schema.Unknown>;
31
+ }>;
32
+ /**
33
+ * Error thrown when the uno server fails to start or when a conversion fails.
34
+ */
35
+ declare class UnoError extends UnoError_base {}
36
+ declare const UnoServer_base: Effect.Service.Class<UnoServer, "libre-convert-effect/index/UnoServer", {
37
+ readonly scoped: Effect.Effect<{
38
+ /**
39
+ * The url of the uno server
40
+ */
41
+ url: string;
42
+ }, _effect_platform_Error0.PlatformError | UnoError, effect_Scope0.Scope | _effect_platform_CommandExecutor0.CommandExecutor>;
43
+ }>;
44
+ /**
45
+ * UnoServer service. The default implementation will try to spawn a new `unoserver` process.
46
+ */
47
+ declare class UnoServer extends UnoServer_base {
48
+ /**
49
+ * Note that while any url can be passed, libreoffice will expect the given files
50
+ * to be on disk and will write them to disk, so to be actually useful the server
51
+ * should probably utilize the same file system as your process.
52
+ *
53
+ * This url can be useful if the uno server is running inside a docker (with a mounted file system)
54
+ */
55
+ static remoteWithURL: (url: string) => Layer.Layer<UnoServer, UnoError, never>;
56
+ /**
57
+ * Static layer that expects the uno server to be running on localhost:2003
58
+ */
59
+ static Remote: Layer.Layer<UnoServer, UnoError, never>;
60
+ }
61
+ declare const UnoClient_base: Effect.Service.Class<UnoClient, "libre-convert-effect/uno/UnoClient", {
62
+ readonly scoped: Effect.Effect<{
63
+ client: HttpClient.HttpClient.With<_effect_platform_HttpClientError0.HttpClientError, never>;
64
+ convert(input: string, output: OutputPath): Effect.Effect<HttpClientResponse.HttpClientResponse, _effect_platform_HttpClientError0.HttpClientError | UnoError | effect_ParseResult0.ParseError, never>;
65
+ compare(input: string, output: string): Effect.Effect<HttpClientResponse.HttpClientResponse, _effect_platform_HttpClientError0.HttpClientError | UnoError | effect_ParseResult0.ParseError, never>;
66
+ }, never, HttpClient.HttpClient | UnoServer>;
67
+ }>;
68
+ declare class UnoClient extends UnoClient_base {}
69
+ //#endregion
70
+ //#region src/index.d.ts
71
+ declare const LibreOfficeCmd_base: Context.ReferenceClass<LibreOfficeCmd, "libre-convert-effect/index/LibreOfficeCmd", string[]>;
72
+ declare class LibreOfficeCmd extends LibreOfficeCmd_base {}
73
+ declare const LibreOffice_base: Effect.Service.Class<LibreOffice, "libre-convert-effect/index/LibreOffice", {
74
+ readonly scoped: Effect.Effect<{
75
+ /**
76
+ * Converts a file to a different format.
77
+ *
78
+ * ### Example
79
+ *
80
+ * ```ts
81
+ * const program = Effect.gen(function* () {
82
+ * const libre = yield* LibreOffice;
83
+ * yield* libre.convertLocalFile("/path/to/input.docx", "/path/to/output.pdf");
84
+ * });
85
+ * ```
86
+ */
87
+ convertLocalFile: (input: string, output: OutputPath) => Effect.Effect<undefined, LibreOfficeError | _effect_platform_Error0.PlatformError, _effect_platform_CommandExecutor0.CommandExecutor>;
88
+ }, never, FileSystem.FileSystem | Path.Path>;
89
+ }>;
90
+ declare class LibreOffice extends LibreOffice_base {
91
+ /**
92
+ * The Uno layer uses a unoserver to convert files. It is much more
93
+ * performant than the cli but requires a unoserver to be running.
94
+ */
95
+ static Uno: Layer.Layer<LibreOffice, never, UnoClient>;
96
+ }
97
+ //#endregion
98
+ export { LibreOffice, LibreOfficeCmd };
package/dist/index.mjs ADDED
@@ -0,0 +1,289 @@
1
+ import { Command, FileSystem, HttpClient, HttpClientRequest, Path } from "@effect/platform";
2
+ import { Context, Data, Effect, Layer, Match, Schedule, Schema, Stream, String, flow } from "effect";
3
+ import { XMLParser } from "fast-xml-parser";
4
+
5
+ //#region src/shared.ts
6
+ var LibreOfficeError = class extends Data.TaggedError("LibreOfficeError") {};
7
+
8
+ //#endregion
9
+ //#region src/uno/schema-utils.ts
10
+ const MemberValue = Schema.Union(Schema.Struct({ int: Schema.Number }), Schema.Struct({ string: Schema.String }));
11
+ const Member = Schema.Struct({
12
+ name: Schema.String,
13
+ value: MemberValue
14
+ });
15
+ const Members = Schema.Array(Member);
16
+ const StructFromMembers = (fields) => Schema.transform(Members, Schema.Struct(fields), {
17
+ strict: false,
18
+ decode: (input) => {
19
+ const output = {};
20
+ for (const member of input) if ("int" in member.value) output[member.name] = member.value.int;
21
+ else if ("string" in member.value) output[member.name] = member.value.string;
22
+ return output;
23
+ },
24
+ encode: (input) => {
25
+ return Object.entries(input).map(([name, value]) => {
26
+ if (typeof value === "number") return {
27
+ name,
28
+ value: { int: value }
29
+ };
30
+ if (typeof value === "string") return {
31
+ name,
32
+ value: { string: value }
33
+ };
34
+ throw new Error(`Unsupported value type for member ${name}: ${typeof value}`);
35
+ });
36
+ }
37
+ });
38
+
39
+ //#endregion
40
+ //#region src/uno/uno-response.ts
41
+ /**
42
+ * Schema for fault response
43
+ *
44
+ * ## Original xml
45
+ * ```xml
46
+ * <?xml version='1.0'?>
47
+ * <methodResponse>
48
+ * <fault>
49
+ * <value>
50
+ * <struct>
51
+ * <member>
52
+ * <name>faultCode</name>
53
+ * <value>
54
+ * <int>1</int>
55
+ * </value>
56
+ * </member>
57
+ * <member>
58
+ * <name>faultString</name>
59
+ * <value>
60
+ * <string>&lt;class 'RuntimeError'&gt;:Path /tmp/test-convert/test.txt does not exist.</string>
61
+ * </value>
62
+ * </member>
63
+ * </struct>
64
+ * </value>
65
+ * </fault>
66
+ * </methodResponse>
67
+ * ```
68
+ */
69
+ const UnoFault = Schema.Struct({ methodResponse: Schema.Struct({ fault: Schema.Struct({ value: Schema.Struct({ struct: Schema.Struct({ member: StructFromMembers({
70
+ faultCode: Schema.Number,
71
+ faultString: Schema.String
72
+ }) }) }) }) }) }).pipe(Schema.transform(Schema.Struct({
73
+ faultCode: Schema.Number,
74
+ faultString: Schema.String
75
+ }), {
76
+ strict: true,
77
+ decode: (input) => input.methodResponse.fault.value.struct.member,
78
+ encode: (input) => ({ methodResponse: { fault: { value: { struct: { member: input } } } } })
79
+ }), Schema.asSchema);
80
+ /**
81
+ * Schema for empty response (success)
82
+ *
83
+ * ## Original xml
84
+ * ```xml
85
+ * <?xml version='1.0'?>
86
+ * <methodResponse>
87
+ * <params>
88
+ * <param>
89
+ * <value>
90
+ * <nil />
91
+ * </value>
92
+ * </param>
93
+ * </params>
94
+ * </methodResponse>
95
+ * ```
96
+ */
97
+ const UnoEmpty = Schema.Struct({ methodResponse: Schema.Struct({ params: Schema.Struct({ param: Schema.Struct({ value: Schema.Struct({ nil: Schema.String }) }) }) }) }).pipe(Schema.asSchema);
98
+ const UnoResponse = Schema.Union(UnoFault, UnoEmpty);
99
+ const decodeUnoResponse = Schema.decodeUnknown(UnoResponse);
100
+
101
+ //#endregion
102
+ //#region src/uno/xml-parser.ts
103
+ const parser = new XMLParser();
104
+ function parseXML(input) {
105
+ return parser.parse(input);
106
+ }
107
+
108
+ //#endregion
109
+ //#region src/uno/uno.ts
110
+ /**
111
+ * Error thrown when the uno server fails to start or when a conversion fails.
112
+ */
113
+ var UnoError = class extends Schema.TaggedError()("UnoError", {
114
+ message: Schema.String,
115
+ reason: Schema.Literal("StartFailed", "Unknown", "InputFileNotFound", "BadOutputExtension", "MethodNotFound", "PermissionDenied"),
116
+ cause: Schema.optional(Schema.Unknown)
117
+ }) {};
118
+ const testRunning = Effect.fn(function* (url) {
119
+ return yield* Effect.tryPromise({
120
+ try: () => fetch(url, {
121
+ method: "POST",
122
+ body: `<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName><params></params></methodCall>`
123
+ }),
124
+ catch: (e) => new UnoError({
125
+ reason: "StartFailed",
126
+ message: globalThis.String(e)
127
+ })
128
+ }).pipe(Effect.flatMap((res) => res.ok ? Effect.void : new UnoError({
129
+ reason: "StartFailed",
130
+ message: "Server not ready"
131
+ })));
132
+ });
133
+ const ensureRunning = flow(testRunning, Effect.retry({
134
+ times: 40,
135
+ schedule: Schedule.spaced("250 millis")
136
+ }));
137
+ /**
138
+ * UnoServer service. The default implementation will try to spawn a new `unoserver` process.
139
+ */
140
+ var UnoServer = class UnoServer extends Effect.Service()("libre-convert-effect/index/UnoServer", { scoped: Effect.gen(function* () {
141
+ const acquire = Effect.gen(function* () {
142
+ const process = yield* Command.start(Command.make("unoserver"));
143
+ yield* ensureRunning(`http://localhost:2003/RPC2`).pipe(Effect.catchAll(() => new UnoError({
144
+ reason: "StartFailed",
145
+ message: "Failed to start server"
146
+ })));
147
+ return process;
148
+ });
149
+ yield* Effect.acquireRelease(acquire, (process) => Effect.ignore(process.kill()));
150
+ return { url: "http://localhost:2003/RPC2" };
151
+ }) }) {
152
+ /**
153
+ * Note that while any url can be passed, libreoffice will expect the given files
154
+ * to be on disk and will write them to disk, so to be actually useful the server
155
+ * should probably utilize the same file system as your process.
156
+ *
157
+ * This url can be useful if the uno server is running inside a docker (with a mounted file system)
158
+ */
159
+ static remoteWithURL = (url) => Layer.scoped(UnoServer, Effect.gen(function* () {
160
+ yield* ensureRunning(url);
161
+ return UnoServer.make({ url });
162
+ }));
163
+ /**
164
+ * Static layer that expects the uno server to be running on localhost:2003
165
+ */
166
+ static Remote = UnoServer.remoteWithURL("http://localhost:2003/RPC2");
167
+ };
168
+ const convertRequest = (input, output) => {
169
+ const body = `<?xml version="1.0"?>
170
+ <methodCall>
171
+ <methodName>convert</methodName>
172
+ <params>
173
+ <param><value><string>${input}</string></value></param>
174
+ <param><value><nil/></value></param>
175
+ <param><value><string>${output}</string></value></param>
176
+ </params>
177
+ </methodCall>
178
+ `;
179
+ return HttpClientRequest.post("").pipe(HttpClientRequest.bodyText(body));
180
+ };
181
+ const compareRequest = (input, output) => {
182
+ const body = `<?xml version="1.0"?>
183
+ <methodCall>
184
+ <methodName>compare</methodName>
185
+ <params>
186
+ <param><value><string>${input}</string></value></param>
187
+ <param><value><nil/></value></param>
188
+ <param><value><string>${output}</string></value></param>
189
+ </params>
190
+ </methodCall>
191
+ `;
192
+ return HttpClientRequest.post("").pipe(HttpClientRequest.bodyText(body));
193
+ };
194
+ const getReason = Match.type().pipe(Match.when({
195
+ faultCode: 1,
196
+ faultString: String.includes("does not exist")
197
+ }, () => "InputFileNotFound"), Match.when({
198
+ faultCode: 1,
199
+ faultString: String.includes("Unknown export file type")
200
+ }, () => "BadOutputExtension"), Match.when({
201
+ faultCode: 1,
202
+ faultString: String.includes("is not supported")
203
+ }, () => "MethodNotFound"), Match.when({
204
+ faultCode: 1,
205
+ faultString: String.includes("PermissionError")
206
+ }, () => "PermissionDenied"), Match.when({
207
+ faultCode: 1,
208
+ faultString: String.includes("Permission denied")
209
+ }, () => "PermissionDenied"), Match.orElse(() => "Unknown"));
210
+ const handleResponse = (response) => response.text.pipe(Effect.map(parseXML), Effect.flatMap(decodeUnoResponse), Effect.flatMap((decoded) => {
211
+ if ("faultString" in decoded) return new UnoError({
212
+ reason: getReason(decoded),
213
+ message: decoded.faultString,
214
+ cause: decoded
215
+ });
216
+ return Effect.succeed(response);
217
+ }));
218
+ var UnoClient = class extends Effect.Service()("libre-convert-effect/uno/UnoClient", { scoped: Effect.gen(function* () {
219
+ const { url } = yield* UnoServer;
220
+ const client = (yield* HttpClient.HttpClient).pipe(HttpClient.mapRequestInput(flow(HttpClientRequest.prependUrl(url))), HttpClient.filterStatusOk);
221
+ return {
222
+ client,
223
+ convert(input, output) {
224
+ return client.execute(convertRequest(input, output)).pipe(Effect.flatMap(handleResponse));
225
+ },
226
+ compare(input, output) {
227
+ return client.execute(compareRequest(input, output)).pipe(Effect.flatMap(handleResponse));
228
+ }
229
+ };
230
+ }) }) {};
231
+
232
+ //#endregion
233
+ //#region src/index.ts
234
+ const runString = (stream) => stream.pipe(Stream.decodeText(), Stream.runFold(String.empty, String.concat));
235
+ var LibreOfficeCmd = class extends Context.Reference()("libre-convert-effect/index/LibreOfficeCmd", { defaultValue: () => ["soffice", "--headless"] }) {};
236
+ var LibreOffice = class LibreOffice extends Effect.Service()("libre-convert-effect/index/LibreOffice", { scoped: Effect.gen(function* () {
237
+ const fs = yield* FileSystem.FileSystem;
238
+ const path = yield* Path.Path;
239
+ const sem = yield* Effect.makeSemaphore(1);
240
+ return { convertLocalFile: Effect.fn(function* (input, output) {
241
+ const [cmd, ...args] = yield* LibreOfficeCmd;
242
+ const parsedInput = path.parse(input);
243
+ const parsedOutput = path.parse(output);
244
+ if (yield* fs.stat(output).pipe(Effect.map((stat) => stat.type === "Directory"), Effect.catchAll(() => Effect.succeed(false)))) return yield* Effect.fail(new LibreOfficeError({
245
+ reason: "BadOutputExtension",
246
+ message: "Output path is a directory"
247
+ }));
248
+ const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "effect-libreoffice-" });
249
+ yield* sem.withPermits(1)(Effect.gen(function* () {
250
+ const process = yield* Command.make(cmd, ...args, "--convert-to", parsedOutput.ext.slice(1), "--outdir", tempDir, input).pipe(Command.start);
251
+ const [exitCode, result] = yield* Effect.all([process.exitCode, runString(process.stderr)], { concurrency: "unbounded" });
252
+ yield* Match.value(String.trim(result)).pipe(Match.when(String.includes("Error: source file could not be loaded"), () => new LibreOfficeError({
253
+ reason: "InputFileNotFound",
254
+ message: result
255
+ })), Match.when(String.includes("Error: no export filter"), () => new LibreOfficeError({
256
+ reason: "BadOutputExtension",
257
+ message: result
258
+ })), Match.when(String.includes("Permission denied"), () => new LibreOfficeError({
259
+ reason: "PermissionDenied",
260
+ message: result
261
+ })), Match.when(String.includes("Error: "), () => new LibreOfficeError({
262
+ reason: "Unknown",
263
+ message: result
264
+ })), Match.orElse(() => Effect.void));
265
+ if (exitCode !== 0) return yield* new LibreOfficeError({
266
+ reason: "Unknown",
267
+ message: result || `Process failed with exit code ${exitCode}`
268
+ });
269
+ const libreOutputPath = path.join(tempDir, String.concat(parsedInput.name, parsedOutput.ext));
270
+ yield* fs.copyFile(libreOutputPath, output);
271
+ }));
272
+ }, Effect.scoped) };
273
+ }) }) {
274
+ /**
275
+ * The Uno layer uses a unoserver to convert files. It is much more
276
+ * performant than the cli but requires a unoserver to be running.
277
+ */
278
+ static Uno = Layer.scoped(LibreOffice, Effect.gen(function* () {
279
+ const client = yield* UnoClient;
280
+ return LibreOffice.make({ convertLocalFile: flow(client.convert, Effect.as(void 0), Effect.mapError((err) => err instanceof UnoError ? new LibreOfficeError(err) : new LibreOfficeError({
281
+ reason: "Unknown",
282
+ message: `Failed to convert file: ${err}`,
283
+ cause: err
284
+ }))) });
285
+ }));
286
+ };
287
+
288
+ //#endregion
289
+ export { LibreOffice, LibreOfficeCmd };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "effect-libreoffice",
3
3
  "type": "module",
4
- "version": "1.0.0",
4
+ "version": "1.0.5",
5
5
  "description": "A effect based LibreOffice converter library",
6
6
  "main": "./dist/index.mjs",
7
7
  "module": "./dist/index.mjs",
@@ -19,6 +19,9 @@
19
19
  ".": "./dist/index.mjs",
20
20
  "./package.json": "./package.json"
21
21
  },
22
+ "files": [
23
+ "dist"
24
+ ],
22
25
  "keywords": [],
23
26
  "dependencies": {
24
27
  "fast-xml-parser": "^5.3.3"
@@ -1,40 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
8
-
9
- jobs:
10
- test:
11
- runs-on: ubuntu-latest
12
-
13
- steps:
14
- - uses: actions/checkout@v4
15
-
16
- - name: Install pnpm
17
- uses: pnpm/action-setup@v4
18
-
19
- - name: Setup Node.js
20
- uses: actions/setup-node@v6
21
- with:
22
- node-version: 24
23
- cache: "pnpm"
24
-
25
- - name: Install dependencies
26
- run: pnpm install
27
-
28
- - name: Install LibreOffice
29
- run: |
30
- sudo apt-get update
31
- sudo apt-get install -y --no-install-recommends libreoffice-writer
32
-
33
- - name: Lint
34
- run: pnpm biome ci .
35
-
36
- - name: Type check
37
- run: pnpm type-check
38
-
39
- - name: Test
40
- run: pnpm test
@@ -1,3 +0,0 @@
1
- {
2
- "typescript.tsdk": "node_modules/typescript/lib"
3
- }
package/Dockerfile DELETED
@@ -1,12 +0,0 @@
1
- FROM alpine:latest
2
-
3
- # install fonts https://wiki.alpinelinux.org/wiki/Fonts
4
- RUN apk add --no-cache font-terminus font-inconsolata font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra
5
-
6
- # install libreoffice + dependencies
7
- RUN apk add --no-cache libreoffice-writer python3 py3-pip openjdk11-jre-headless
8
-
9
- # install unoserver via pip
10
- RUN pip install unoserver --break-system-packages
11
-
12
- CMD ["unoserver", "--interface", "0.0.0.0"]
package/biome.json DELETED
@@ -1,43 +0,0 @@
1
- {
2
- "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
3
- "vcs": {
4
- "enabled": true,
5
- "clientKind": "git",
6
- "useIgnoreFile": true
7
- },
8
- "files": {
9
- "ignoreUnknown": true
10
- },
11
- "formatter": {
12
- "enabled": true,
13
- "indentStyle": "space"
14
- },
15
- "linter": {
16
- "enabled": true,
17
- "rules": {
18
- "recommended": true,
19
- "suspicious": {
20
- "noShadowRestrictedNames": "off"
21
- },
22
- "correctness": {
23
- "noUnusedVariables": {
24
- "level": "warn",
25
- "fix": "none"
26
- }
27
- }
28
- }
29
- },
30
- "javascript": {
31
- "formatter": {
32
- "quoteStyle": "double"
33
- }
34
- },
35
- "assist": {
36
- "enabled": true,
37
- "actions": {
38
- "source": {
39
- "organizeImports": "on"
40
- }
41
- }
42
- }
43
- }
package/compose.yml DELETED
@@ -1,19 +0,0 @@
1
- services:
2
- # unoserver:
3
- # image: libreofficedocker/libreoffice-unoserver:3.22
4
- # ports:
5
- # - "2004:2004" # uno rest api
6
- # - "2003:2003" # uno api
7
- unoserver:
8
- build: .
9
- ports:
10
- - "2003:2003" # uno api
11
- volumes:
12
- - /tmp/test-convert:/tmp/test-convert
13
- user: "1000:1000"
14
- environment:
15
- - HOME=/tmp
16
- develop:
17
- watch:
18
- - action: rebuild
19
- path: ./Dockerfile
@@ -1,3 +0,0 @@
1
- FROM ubuntu:24.04
2
-
3
- RUN apt-get update && apt-get install -y libreoffice-writer --no-install-recommends
@@ -1,5 +0,0 @@
1
- services:
2
- unoserver:
3
- build: ./uno.Dockerfile
4
- api:
5
- build: ./api.Dockerfile
@@ -1,9 +0,0 @@
1
- FROM alpine:latest
2
-
3
- # install libreoffice + dependencies
4
- RUN apk add --no-cache libreoffice-writer python3 py3-pip openjdk11-jre-headless
5
-
6
- # install unoserver via pip
7
- RUN pip install unoserver --break-system-packages
8
-
9
- CMD ["unoserver"]
package/fixtures/test.txt DELETED
@@ -1,7 +0,0 @@
1
- Hi there
2
-
3
- This is a test file.
4
-
5
- This should be converted to PDF.
6
-
7
- Have fun!