@telorun/pdf 0.2.0 → 0.3.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/README.md +2 -141
- package/dist/form-fields-controller.d.ts +0 -4
- package/dist/form-fields-controller.js +3 -7
- package/dist/rasterizer-controller.d.ts +0 -4
- package/dist/rasterizer-controller.js +4 -7
- package/package.json +2 -2
- package/src/form-fields-controller.ts +3 -10
- package/src/rasterizer-controller.ts +4 -10
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Built to be language-agnostic and infinitely extensible.
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
# Reconcile your manifest into a running backend
|
|
26
|
-
$ telo ./examples/hello-api
|
|
26
|
+
$ telo ./examples/hello-api
|
|
27
27
|
|
|
28
28
|
{"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:8844"}
|
|
29
29
|
```
|
|
@@ -44,146 +44,7 @@ $ telo ./examples/hello-api.yaml
|
|
|
44
44
|
|
|
45
45
|
## Example manifest
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```yaml
|
|
50
|
-
kind: Telo.Application
|
|
51
|
-
metadata:
|
|
52
|
-
name: feedback
|
|
53
|
-
version: 1.0.0
|
|
54
|
-
description: |
|
|
55
|
-
A complete feedback collection REST API — no code, pure YAML.
|
|
56
|
-
Persists entries to SQLite and serves them over HTTP.
|
|
57
|
-
imports:
|
|
58
|
-
Http: std/http-server@0.11.0
|
|
59
|
-
Sql: std/sql@0.9.0
|
|
60
|
-
targets:
|
|
61
|
-
- !ref Migrations
|
|
62
|
-
- !ref Server
|
|
63
|
-
---
|
|
64
|
-
# SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
|
|
65
|
-
kind: Sql.Connection
|
|
66
|
-
metadata:
|
|
67
|
-
name: Db
|
|
68
|
-
driver: sqlite
|
|
69
|
-
file: ./tmp/feedback.db
|
|
70
|
-
---
|
|
71
|
-
# Migrations: applied automatically before the server starts
|
|
72
|
-
kind: Sql.Migrations
|
|
73
|
-
metadata:
|
|
74
|
-
name: Migrations
|
|
75
|
-
connection: !ref Db
|
|
76
|
-
---
|
|
77
|
-
kind: Sql.Migration
|
|
78
|
-
metadata:
|
|
79
|
-
name: Migration_20260413_182154_CreateFeedback
|
|
80
|
-
version: 20260413_182154_CreateFeedback
|
|
81
|
-
sql: |
|
|
82
|
-
CREATE TABLE IF NOT EXISTS feedback (
|
|
83
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
84
|
-
text TEXT NOT NULL,
|
|
85
|
-
source TEXT,
|
|
86
|
-
score INTEGER NOT NULL DEFAULT 0,
|
|
87
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
88
|
-
)
|
|
89
|
-
---
|
|
90
|
-
kind: Http.Server
|
|
91
|
-
metadata:
|
|
92
|
-
name: Server
|
|
93
|
-
baseUrl: http://localhost:8844
|
|
94
|
-
port: 8844
|
|
95
|
-
logger: true
|
|
96
|
-
openapi:
|
|
97
|
-
info:
|
|
98
|
-
title: Feedback API
|
|
99
|
-
version: 1.0.0
|
|
100
|
-
mounts:
|
|
101
|
-
- path: /v1
|
|
102
|
-
mount: !ref FeedbackRoutes
|
|
103
|
-
---
|
|
104
|
-
kind: Http.Api
|
|
105
|
-
metadata:
|
|
106
|
-
name: FeedbackRoutes
|
|
107
|
-
routes:
|
|
108
|
-
# POST /v1/feedback — insert a new entry, score derived from body length heuristic
|
|
109
|
-
- request:
|
|
110
|
-
path: /feedback
|
|
111
|
-
method: POST
|
|
112
|
-
schema:
|
|
113
|
-
body:
|
|
114
|
-
type: object
|
|
115
|
-
properties:
|
|
116
|
-
text:
|
|
117
|
-
type: string
|
|
118
|
-
minLength: 1
|
|
119
|
-
source:
|
|
120
|
-
type: string
|
|
121
|
-
required: [ text ]
|
|
122
|
-
handler:
|
|
123
|
-
kind: Sql.Exec
|
|
124
|
-
connection: !ref Db
|
|
125
|
-
inputs:
|
|
126
|
-
sql: "INSERT INTO feedback (text, source, score) VALUES (?, ?, ?)"
|
|
127
|
-
bindings:
|
|
128
|
-
- "${{ request.body.text }}"
|
|
129
|
-
- "${{ request.body.source }}"
|
|
130
|
-
- "${{ size(request.body.text) }}"
|
|
131
|
-
response:
|
|
132
|
-
- status: 201
|
|
133
|
-
headers:
|
|
134
|
-
Content-Type: application/json
|
|
135
|
-
body:
|
|
136
|
-
ok: true
|
|
137
|
-
message: Feedback received
|
|
138
|
-
|
|
139
|
-
# GET /v1/feedback — list all entries, newest first
|
|
140
|
-
- request:
|
|
141
|
-
path: /feedback
|
|
142
|
-
method: GET
|
|
143
|
-
handler:
|
|
144
|
-
kind: Sql.Select
|
|
145
|
-
connection: !ref Db
|
|
146
|
-
from: feedback
|
|
147
|
-
columns: [ id, text, source, score, created_at ]
|
|
148
|
-
orderBy:
|
|
149
|
-
- { column: created_at, direction: desc }
|
|
150
|
-
response:
|
|
151
|
-
- status: 200
|
|
152
|
-
headers:
|
|
153
|
-
Content-Type: application/json
|
|
154
|
-
body: "${{ result.rows }}"
|
|
155
|
-
|
|
156
|
-
# GET /v1/feedback/{id} — fetch a single entry
|
|
157
|
-
- request:
|
|
158
|
-
path: /feedback/{id}
|
|
159
|
-
method: GET
|
|
160
|
-
schema:
|
|
161
|
-
params:
|
|
162
|
-
type: object
|
|
163
|
-
properties:
|
|
164
|
-
id:
|
|
165
|
-
type: integer
|
|
166
|
-
required: [ id ]
|
|
167
|
-
handler:
|
|
168
|
-
kind: Sql.Select
|
|
169
|
-
connection: !ref Db
|
|
170
|
-
from: feedback
|
|
171
|
-
columns: [ id, text, source, score, created_at ]
|
|
172
|
-
where:
|
|
173
|
-
- { column: id, op: "=", value: "${{ request.params.id }}" }
|
|
174
|
-
response:
|
|
175
|
-
- status: 200
|
|
176
|
-
when: "size(result.rows) > 0"
|
|
177
|
-
headers:
|
|
178
|
-
Content-Type: application/json
|
|
179
|
-
body: "${{ result.rows[0] }}"
|
|
180
|
-
- status: 404
|
|
181
|
-
headers:
|
|
182
|
-
Content-Type: application/json
|
|
183
|
-
body:
|
|
184
|
-
ok: false
|
|
185
|
-
message: Not found
|
|
186
|
-
```
|
|
47
|
+
See [examples/](./examples/) for a list of working applications.
|
|
187
48
|
|
|
188
49
|
## Status
|
|
189
50
|
|
|
@@ -39,8 +39,4 @@ declare class PdfFormFields implements ResourceInstance<FormFieldsInputs, FormFi
|
|
|
39
39
|
}
|
|
40
40
|
export declare function register(ctx: ControllerContext): void;
|
|
41
41
|
export declare function create(resource: FormFieldsResource, ctx: ResourceContext): Promise<PdfFormFields>;
|
|
42
|
-
export declare const schema: {
|
|
43
|
-
type: string;
|
|
44
|
-
additionalProperties: boolean;
|
|
45
|
-
};
|
|
46
42
|
export {};
|
|
@@ -14,10 +14,10 @@ class PdfFormFields {
|
|
|
14
14
|
}
|
|
15
15
|
async invoke(inputs) {
|
|
16
16
|
const label = `Pdf.FormFields "${this.resource.metadata.name}"`;
|
|
17
|
+
// An `inputs.scale` is declared on `inputType` with `exclusiveMinimum: 0` and
|
|
18
|
+
// enforced by the kernel's contract binding; the resource-level fallback is
|
|
19
|
+
// checked by the config schema. Neither needs re-checking here.
|
|
17
20
|
const scale = inputs?.scale ?? this.resource.scale ?? 1;
|
|
18
|
-
if (typeof scale !== "number" || !(scale > 0)) {
|
|
19
|
-
throw new InvokeError("ERR_INVALID_INPUT", `${label}: 'scale' must be a positive number; got ${scale}.`);
|
|
20
|
-
}
|
|
21
21
|
const data = inputs?.data;
|
|
22
22
|
if (!(data instanceof Uint8Array)) {
|
|
23
23
|
throw new InvokeError("ERR_INVALID_INPUT", `${label}: 'data' must be a Uint8Array of PDF bytes; got ${typeof data}.`);
|
|
@@ -78,7 +78,3 @@ export function register(ctx) { }
|
|
|
78
78
|
export async function create(resource, ctx) {
|
|
79
79
|
return new PdfFormFields(resource);
|
|
80
80
|
}
|
|
81
|
-
export const schema = {
|
|
82
|
-
type: "object",
|
|
83
|
-
additionalProperties: true,
|
|
84
|
-
};
|
|
@@ -36,8 +36,4 @@ declare class PdfRasterizer implements ResourceInstance<RasterizerInputs, Raster
|
|
|
36
36
|
}
|
|
37
37
|
export declare function register(ctx: ControllerContext): void;
|
|
38
38
|
export declare function create(resource: RasterizerResource, ctx: ResourceContext): Promise<PdfRasterizer>;
|
|
39
|
-
export declare const schema: {
|
|
40
|
-
type: string;
|
|
41
|
-
additionalProperties: boolean;
|
|
42
|
-
};
|
|
43
39
|
export {};
|
|
@@ -32,10 +32,11 @@ class PdfRasterizer {
|
|
|
32
32
|
if (!(data instanceof Uint8Array)) {
|
|
33
33
|
throw new InvokeError("ERR_INVALID_INPUT", `Pdf.Rasterizer "${name}": 'data' must be a Uint8Array of PDF bytes; got ${typeof data}.`);
|
|
34
34
|
}
|
|
35
|
+
// `page` is declared on `inputType` as an integer with `minimum: 1` and
|
|
36
|
+
// enforced by the kernel's contract binding, so only the range check the
|
|
37
|
+
// schema cannot express — page vs. the document's actual page count — is
|
|
38
|
+
// still this controller's to make (below).
|
|
35
39
|
const pageNumber = inputs.page ?? 1;
|
|
36
|
-
if (!Number.isInteger(pageNumber) || pageNumber < 1) {
|
|
37
|
-
throw new InvokeError("ERR_INVALID_INPUT", `Pdf.Rasterizer "${name}": 'page' must be a positive integer; got ${pageNumber}.`);
|
|
38
|
-
}
|
|
39
40
|
const task = loadPdf(data);
|
|
40
41
|
try {
|
|
41
42
|
const doc = await task.promise.catch((err) => {
|
|
@@ -109,7 +110,3 @@ export function register(ctx) { }
|
|
|
109
110
|
export async function create(resource, ctx) {
|
|
110
111
|
return new PdfRasterizer(resource);
|
|
111
112
|
}
|
|
112
|
-
export const schema = {
|
|
113
|
-
type: "object",
|
|
114
|
-
additionalProperties: true,
|
|
115
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/pdf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "PDF primitives — rasterize pages to PNG images and author editable AcroForm fields.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^20.0.0",
|
|
49
49
|
"typescript": "^5.0.0",
|
|
50
|
-
"@telorun/sdk": "0.
|
|
50
|
+
"@telorun/sdk": "0.61.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@telorun/sdk": "*"
|
|
@@ -41,13 +41,10 @@ class PdfFormFields implements ResourceInstance<FormFieldsInputs, FormFieldsOutp
|
|
|
41
41
|
|
|
42
42
|
async invoke(inputs: FormFieldsInputs): Promise<FormFieldsOutputs> {
|
|
43
43
|
const label = `Pdf.FormFields "${this.resource.metadata.name}"`;
|
|
44
|
+
// An `inputs.scale` is declared on `inputType` with `exclusiveMinimum: 0` and
|
|
45
|
+
// enforced by the kernel's contract binding; the resource-level fallback is
|
|
46
|
+
// checked by the config schema. Neither needs re-checking here.
|
|
44
47
|
const scale = inputs?.scale ?? this.resource.scale ?? 1;
|
|
45
|
-
if (typeof scale !== "number" || !(scale > 0)) {
|
|
46
|
-
throw new InvokeError(
|
|
47
|
-
"ERR_INVALID_INPUT",
|
|
48
|
-
`${label}: 'scale' must be a positive number; got ${scale}.`,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
48
|
const data = inputs?.data;
|
|
52
49
|
if (!(data instanceof Uint8Array)) {
|
|
53
50
|
throw new InvokeError(
|
|
@@ -142,7 +139,3 @@ export async function create(
|
|
|
142
139
|
return new PdfFormFields(resource);
|
|
143
140
|
}
|
|
144
141
|
|
|
145
|
-
export const schema = {
|
|
146
|
-
type: "object",
|
|
147
|
-
additionalProperties: true,
|
|
148
|
-
};
|
|
@@ -59,13 +59,11 @@ class PdfRasterizer implements ResourceInstance<RasterizerInputs, RasterizerOutp
|
|
|
59
59
|
`Pdf.Rasterizer "${name}": 'data' must be a Uint8Array of PDF bytes; got ${typeof data}.`,
|
|
60
60
|
);
|
|
61
61
|
}
|
|
62
|
+
// `page` is declared on `inputType` as an integer with `minimum: 1` and
|
|
63
|
+
// enforced by the kernel's contract binding, so only the range check the
|
|
64
|
+
// schema cannot express — page vs. the document's actual page count — is
|
|
65
|
+
// still this controller's to make (below).
|
|
62
66
|
const pageNumber = inputs.page ?? 1;
|
|
63
|
-
if (!Number.isInteger(pageNumber) || pageNumber < 1) {
|
|
64
|
-
throw new InvokeError(
|
|
65
|
-
"ERR_INVALID_INPUT",
|
|
66
|
-
`Pdf.Rasterizer "${name}": 'page' must be a positive integer; got ${pageNumber}.`,
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
67
|
|
|
70
68
|
const task = loadPdf(data);
|
|
71
69
|
try {
|
|
@@ -166,7 +164,3 @@ export async function create(
|
|
|
166
164
|
return new PdfRasterizer(resource);
|
|
167
165
|
}
|
|
168
166
|
|
|
169
|
-
export const schema = {
|
|
170
|
-
type: "object",
|
|
171
|
-
additionalProperties: true,
|
|
172
|
-
};
|