@tsln/max-api-types 0.0.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/index.d.ts +160 -0
  4. package/package.json +41 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mateo Murphy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @tsln/max-api-types
2
+
3
+ Type declarations for `max-api`, the module Max's `[node.script]` object provides to the
4
+ Node.js process it runs (Node for Max), transcribed from the
5
+ [reference](https://docs.cycling74.com/apiref/nodeformax/).
6
+
7
+ This is the Node.js side of Max. For scripts in `[v8]`, `[js]` and their UI variants see
8
+ [`@tsln/max-types`](../max-types); the two declare globals of the same names and can't be
9
+ loaded together. A project with both kinds of script gives each its own folder and
10
+ `tsconfig.json`; the [repository README](../../README.md#using-the-v8-and-nodescript-types-in-one-project)
11
+ shows the layout.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ npm install --save-dev @tsln/max-api-types @types/node
17
+ ```
18
+
19
+ `max-api` isn't a package to install: `[node.script]` injects it. The declarations are ambient,
20
+ so list the package in `types` and require the module as usual:
21
+
22
+ ```jsonc
23
+ {
24
+ "compilerOptions": {
25
+ "types": ["node", "@tsln/max-api-types"]
26
+ }
27
+ }
28
+ ```
29
+
30
+ ```ts
31
+ import maxAPI = require("max-api");
32
+
33
+ maxAPI.addHandlers({
34
+ bang: () => maxAPI.outletBang(),
35
+ number: (value) => maxAPI.outlet(value * 2), // value: number
36
+ list: (...values) => maxAPI.outlet(values.length), // values: (string | number)[]
37
+ dict: (dict) => maxAPI.post(dict.gain), // dict: JSONObject
38
+ gain: (db: number) => { ... }, // a message of the script's own: `gain -6`
39
+ [maxAPI.MESSAGE_TYPES.ALL]: (handled, ...args) => {
40
+ if (!handled) maxAPI.post("unhandled:", args, maxAPI.POST_LEVELS.WARN);
41
+ },
42
+ });
43
+ ```
44
+
45
+ ## Compared to `@types/max-api`
46
+
47
+ DefinitelyTyped has typings for the same module. These differ in two ways:
48
+
49
+ - The module is declared with `export =`, so `import maxAPI = require("max-api")` gives the API
50
+ directly, as it does at runtime, instead of an object with a `default` property.
51
+ - Handlers for the predefined selectors (`bang`, `number`, `list`, `dict`, `all`) are typed with
52
+ what they receive, through `addHandler` and `addHandlers` alike. Handlers for messages of the
53
+ script's own stay open, as `(...args: any[]) => void`.
54
+
55
+ `MAX_ENV`, `MESSAGE_TYPES` and `POST_LEVELS` are declared as the objects of strings they are at
56
+ runtime rather than as enums, so the plain strings work wherever the constants do.
57
+ `process.env.MAX_ENV` is typed with the `MAX_ENV` values.
package/index.d.ts ADDED
@@ -0,0 +1,160 @@
1
+ // Type declarations for the "max-api" module that Max's [node.script] object provides to the
2
+ // Node.js process it runs. Transcribed from https://docs.cycling74.com/apiref/nodeformax/
3
+ //
4
+ // This is a Node.js API, not the [v8]/[js] one: it goes with @types/node, and must not be
5
+ // loaded together with @tsln/max-types, which declares globals of the same names.
6
+
7
+ declare module "max-api" {
8
+ // ---- values ----
9
+
10
+ /** A single Max atom, as they arrive in a list or a message. */
11
+ type Atom = string | number;
12
+
13
+ type JSONPrimitive = string | number | boolean | null;
14
+ type JSONArray = JSONValue[];
15
+ interface JSONObject {
16
+ [key: string]: JSONValue | undefined;
17
+ }
18
+ type JSONValue = JSONPrimitive | JSONArray | JSONObject;
19
+
20
+ /** What post() accepts: atoms, a list of atoms, or something JSON-like. */
21
+ type Anything = string | number | Atom[] | JSONObject | JSONArray;
22
+
23
+ // ---- constants ----
24
+ //
25
+ // The reference presents these as enums; at runtime they are plain objects of strings,
26
+ // which is how they are declared here so that the strings themselves can be used too.
27
+
28
+ /** Values Node for Max sets `process.env.MAX_ENV` to. */
29
+ const MAX_ENV: {
30
+ /** node.script running from within Max */
31
+ readonly MAX: "max";
32
+ /** node.script running from within Max for Live */
33
+ readonly MAX_FOR_LIVE: "maxforlive";
34
+ /** node.script running from within a standalone application */
35
+ readonly STANDALONE: "max:standalone";
36
+ };
37
+ type MaxEnv = (typeof MAX_ENV)[keyof typeof MAX_ENV];
38
+
39
+ /** The predefined selectors a handler can be registered for. */
40
+ const MESSAGE_TYPES: {
41
+ /** Every message, after the more specific handlers */
42
+ readonly ALL: "all";
43
+ /** A bang */
44
+ readonly BANG: "bang";
45
+ /** A dictionary */
46
+ readonly DICT: "dict";
47
+ /** A list (a message starting with a number) */
48
+ readonly LIST: "list";
49
+ /** A single number */
50
+ readonly NUMBER: "number";
51
+ };
52
+ type MessageType = (typeof MESSAGE_TYPES)[keyof typeof MESSAGE_TYPES];
53
+
54
+ /** Log levels for post(), given as its last argument. */
55
+ const POST_LEVELS: {
56
+ readonly ERROR: "error";
57
+ readonly INFO: "info";
58
+ readonly WARN: "warn";
59
+ };
60
+ type PostLevel = (typeof POST_LEVELS)[keyof typeof POST_LEVELS];
61
+
62
+ // ---- handlers ----
63
+
64
+ /** What a handler receives for each predefined selector. */
65
+ interface MessageHandlers {
66
+ /**
67
+ * Called for every message, after any more specific handler. `handled` says whether
68
+ * another handler already took the message; the rest is the message as received.
69
+ */
70
+ all: (handled: boolean, ...args: any[]) => void;
71
+ bang: () => void;
72
+ /** The dictionary's contents. */
73
+ dict: (dict: JSONObject) => void;
74
+ /** The elements of the list. */
75
+ list: (...values: Atom[]) => void;
76
+ number: (value: number) => void;
77
+ }
78
+
79
+ /** A handler for a message of the script's own, `selector arg1 arg2 ...`. */
80
+ type AnyHandler = (...args: any[]) => void;
81
+
82
+ /** Either a predefined selector or a message name of the script's own. */
83
+ type MaxFunctionSelector = MessageType | (string & {});
84
+
85
+ /** The handler type for a selector: typed for the predefined ones, open for the rest. */
86
+ type MaxFunctionHandler<S extends string = string> = S extends keyof MessageHandlers
87
+ ? MessageHandlers[S]
88
+ : AnyHandler;
89
+
90
+ /** Handlers by selector, as addHandlers() takes them. */
91
+ interface HandlerMap extends Partial<MessageHandlers> {
92
+ [selector: string]: AnyHandler | undefined;
93
+ }
94
+
95
+ /**
96
+ * Registers a handler for a selector: a predefined MESSAGE_TYPES value, or the first
97
+ * word of a message of the script's own.
98
+ * @example
99
+ * maxAPI.addHandler("list", (...values) => maxAPI.outlet(values.length));
100
+ * maxAPI.addHandler("gain", (db: number) => { ... });
101
+ */
102
+ function addHandler<S extends string>(selector: S, handler: MaxFunctionHandler<S>): void;
103
+
104
+ /**
105
+ * Registers several handlers at once, keyed by selector.
106
+ * @example
107
+ * maxAPI.addHandlers({
108
+ * bang: () => maxAPI.outletBang(),
109
+ * [maxAPI.MESSAGE_TYPES.ALL]: (handled, ...args) => { if (!handled) maxAPI.post(args); },
110
+ * });
111
+ */
112
+ function addHandlers(handlers: HandlerMap): void;
113
+
114
+ /** Removes one handler previously registered for the selector. */
115
+ function removeHandler<S extends string>(selector: S, handler: MaxFunctionHandler<S>): void;
116
+
117
+ /** Removes every handler registered for the selector. */
118
+ function removeHandlers(selector: MaxFunctionSelector): void;
119
+
120
+ // ---- output ----
121
+
122
+ /**
123
+ * Sends the values out of the [node.script] outlet: atoms as a message or list, arrays
124
+ * and objects as a dictionary. Resolves once Max has received them.
125
+ */
126
+ function outlet(...args: JSONValue[]): Promise<void>;
127
+
128
+ /** Sends a bang out of the [node.script] outlet. */
129
+ function outletBang(): Promise<void>;
130
+
131
+ /**
132
+ * Posts to the Max console. A POST_LEVELS value as the last argument sets the level.
133
+ * @example
134
+ * maxAPI.post("something went wrong", maxAPI.POST_LEVELS.ERROR);
135
+ */
136
+ function post(...args: (Anything | PostLevel)[]): Promise<void>;
137
+
138
+ // ---- dictionaries ----
139
+
140
+ /** Reads the contents of the named [dict]. */
141
+ function getDict(id: string): Promise<JSONObject>;
142
+
143
+ /** Replaces the contents of the named [dict], and resolves with the new contents. */
144
+ function setDict(id: string, dict: JSONObject): Promise<JSONObject>;
145
+
146
+ /**
147
+ * Sets one value in the named [dict] at a path such as `"a.b.c"` or `"items[2]"`, and
148
+ * resolves with the whole new contents.
149
+ */
150
+ function updateDict(id: string, updatePath: string, updateValue: JSONValue): Promise<JSONObject>;
151
+
152
+ global {
153
+ namespace NodeJS {
154
+ interface ProcessEnv {
155
+ /** Set by Node for Max to say what the script is running inside; see MAX_ENV. */
156
+ MAX_ENV?: MaxEnv;
157
+ }
158
+ }
159
+ }
160
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@tsln/max-api-types",
3
+ "version": "0.0.0",
4
+ "description": "Type declarations for the max-api module of Max's [node.script] object (Node for Max)",
5
+ "license": "MIT",
6
+ "author": "Mateo Murphy",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/tsln-lab/max-packages.git",
10
+ "directory": "packages/max-api-types"
11
+ },
12
+ "keywords": [
13
+ "max",
14
+ "msp",
15
+ "max-msp",
16
+ "node-for-max",
17
+ "node.script",
18
+ "max-api",
19
+ "cycling74",
20
+ "typescript",
21
+ "types"
22
+ ],
23
+ "types": "./index.d.ts",
24
+ "files": [
25
+ "*.d.ts",
26
+ "README.md",
27
+ "CHANGELOG.md",
28
+ "LICENSE"
29
+ ],
30
+ "sideEffects": false,
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.0.0",
36
+ "typescript": "7.0.2"
37
+ },
38
+ "scripts": {
39
+ "typecheck": "tsc -p tsconfig.json"
40
+ }
41
+ }