@defold-typescript/types 0.24.0 → 0.25.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/api-signatures.json +44 -44
- package/api-targets.json +57 -0
- package/generated/editor-vm/http.d.ts +56 -0
- package/generated/editor-vm/json.d.ts +10 -0
- package/generated/editor-vm/localization.d.ts +42 -0
- package/generated/editor-vm/tilemap_tiles.d.ts +80 -0
- package/generated/editor-vm/zip.d.ts +10 -0
- package/generated/editor-vm/zlib.d.ts +24 -0
- package/generated/editor.d.ts +1092 -0
- package/generated/go.d.ts +22 -22
- package/generated/kinds/editor-script.d.ts +13 -0
- package/generated/kinds/gui-script.d.ts +1 -0
- package/generated/kinds/render-script.d.ts +1 -0
- package/generated/kinds/script.d.ts +1 -0
- package/generated/versions/defold-1.12.4/go.d.ts +22 -22
- package/index.d.ts +6 -0
- package/package.json +9 -1
- package/scripts/materialize-version.ts +54 -13
- package/scripts/regen.ts +425 -42
- package/scripts/sync-api-docs.ts +82 -6
- package/src/core-types.ts +15 -1
- package/src/editor-overloads.d.ts +33 -0
- package/src/editor-vm-globals.d.ts +200 -0
- package/src/editor-vm-types.ts +44 -0
- package/src/editor.ts +117 -16
- package/src/emit-dts.ts +292 -79
- package/src/engine-globals.d.ts +2 -2
- package/src/go-overloads.d.ts +6 -6
- package/src/index.ts +12 -0
- package/src/msg-overloads.d.ts +3 -3
- package/src/scene-addresses.d.ts +62 -0
- package/src/url-parameters.ts +174 -0
- package/url-parameters.json +285 -0
package/scripts/sync-api-docs.ts
CHANGED
|
@@ -24,6 +24,10 @@ export interface SyncManifestEntry {
|
|
|
24
24
|
// docs (e.g. `sys`) is reassembled before parse, so the emitter still sees one
|
|
25
25
|
// fixture, one namespace.
|
|
26
26
|
readonly mergeEntries?: readonly string[];
|
|
27
|
+
// Extract only this namespace's elements from `zipEntry` and rewrite
|
|
28
|
+
// `info.namespace`. One upstream document that carries several top-level
|
|
29
|
+
// namespaces (the editor apidoc) becomes one fixture per namespace.
|
|
30
|
+
readonly split?: true;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
// namespace -> ref-doc.zip entry. Entry paths do not match the namespace (gui ->
|
|
@@ -122,7 +126,6 @@ const LUA_NAMESPACE = /^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)*$/;
|
|
|
122
126
|
// `EMPTY_BY_UPSTREAM` allowlist shape). The upstream-coverage guard treats these
|
|
123
127
|
// as covered. Adding a namespace here is a deliberate, reviewed act.
|
|
124
128
|
export const IGNORED_UPSTREAM: ReadonlyMap<string, string> = new Map([
|
|
125
|
-
["editor", "editor-scripting API (editor.apidoc), not a runtime game namespace"],
|
|
126
129
|
["engine", "CLI/engine env doc, not a runtime Lua namespace"],
|
|
127
130
|
[
|
|
128
131
|
"builtins",
|
|
@@ -158,6 +161,46 @@ export const LUA_STDLIB_MANIFEST: readonly SyncManifestEntry[] = [
|
|
|
158
161
|
entry("package", "doc/lua_package.doc_h_doc.json"),
|
|
159
162
|
];
|
|
160
163
|
|
|
164
|
+
// The editor-scripting API. It runs in the Defold *editor's* Lua VM, not the
|
|
165
|
+
// game runtime, so it is deliberately not a `MODULE_MANIFEST` citizen: that
|
|
166
|
+
// manifest drives every runtime kind's universal import set, the per-version
|
|
167
|
+
// targets, `api-availability.json` / `api-signatures.json` and the docs-site API
|
|
168
|
+
// pages, none of which describe the editor. It still syncs and emits through the
|
|
169
|
+
// same machinery (`EDITOR_MODULE_MANIFEST` in regen.ts writes
|
|
170
|
+
// `generated/editor.d.ts`), reached only through the `editor-script` kind index.
|
|
171
|
+
export const EDITOR_MANIFEST: readonly SyncManifestEntry[] = [
|
|
172
|
+
entry("editor", "doc/editor.apidoc_doc.json"),
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
// The non-`editor.*` namespaces the same editor apidoc carries. Each is split
|
|
176
|
+
// out of that one document into its own fixture; the `editor` entry above keeps
|
|
177
|
+
// vendoring the document whole, so `editor_doc.json` is unaffected.
|
|
178
|
+
//
|
|
179
|
+
// Deliberately absent from `COVERAGE_MANIFEST`, `UPSTREAM_MAPPED_NAMESPACES`
|
|
180
|
+
// and `syncedDocs`: these exist only in the editor VM, so marking them mapped
|
|
181
|
+
// would tell the upstream-coverage guard the *runtime* covers `zip`, `pprint`
|
|
182
|
+
// and `tilemap.tiles` and silently suppress a future real miss. Fixture paths
|
|
183
|
+
// are explicit because the default would collide with the unrelated runtime
|
|
184
|
+
// `http_doc.json` / `json_doc.json` / `zlib_doc.json`.
|
|
185
|
+
export const EDITOR_VM_MANIFEST: readonly SyncManifestEntry[] = [
|
|
186
|
+
editorVm("http"),
|
|
187
|
+
editorVm("json"),
|
|
188
|
+
editorVm("localization"),
|
|
189
|
+
editorVm("zip"),
|
|
190
|
+
editorVm("zlib"),
|
|
191
|
+
editorVm("pprint"),
|
|
192
|
+
editorVm("tilemap.tiles"),
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
function editorVm(namespace: string): SyncManifestEntry {
|
|
196
|
+
return {
|
|
197
|
+
namespace,
|
|
198
|
+
zipEntry: "doc/editor.apidoc_doc.json",
|
|
199
|
+
fixture: `fixtures/defold-${DEFOLD_VERSION}/editor_${namespace.replace(/\./g, "_")}_doc.json`,
|
|
200
|
+
split: true,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
161
204
|
function entry(
|
|
162
205
|
namespace: string,
|
|
163
206
|
zipEntry: string,
|
|
@@ -196,6 +239,21 @@ function ext(namespace: string, repo: string, tag: string, path: string): Extens
|
|
|
196
239
|
};
|
|
197
240
|
}
|
|
198
241
|
|
|
242
|
+
// Every vendored fixture set the coverage report audits, and the namespaces it
|
|
243
|
+
// counts as reached from upstream. Composed once here so the `--check` run and
|
|
244
|
+
// the guard over it cannot disagree about which sets are in scope.
|
|
245
|
+
export const COVERAGE_MANIFEST: readonly { readonly namespace: string }[] = [
|
|
246
|
+
...SYNC_MANIFEST,
|
|
247
|
+
...EXTENSION_MANIFEST,
|
|
248
|
+
...EDITOR_MANIFEST,
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
export const UPSTREAM_MAPPED_NAMESPACES: ReadonlySet<string> = new Set(
|
|
252
|
+
[...SYNC_MANIFEST, ...EXTENSION_MANIFEST, ...LUA_STDLIB_MANIFEST, ...EDITOR_MANIFEST].map(
|
|
253
|
+
(e) => e.namespace,
|
|
254
|
+
),
|
|
255
|
+
);
|
|
256
|
+
|
|
199
257
|
export const extensionRawUrl = (e: ExtensionManifestEntry): string =>
|
|
200
258
|
`https://raw.githubusercontent.com/${e.repo}/${e.tag}/${e.path}`;
|
|
201
259
|
|
|
@@ -328,6 +386,21 @@ function normalizedArguments(value: unknown): string {
|
|
|
328
386
|
);
|
|
329
387
|
}
|
|
330
388
|
|
|
389
|
+
// Segment-wise ownership: `pprint` owns `pprint` and `pprint.x` but never
|
|
390
|
+
// `pprintx`, and `tilemap.tiles` never claims `tilemap.set_tile`.
|
|
391
|
+
function ownsElement(name: string, namespace: string): boolean {
|
|
392
|
+
return name === namespace || name.startsWith(`${namespace}.`);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function splitNamespace(contents: string, namespace: string): string {
|
|
396
|
+
const doc = JSON.parse(contents);
|
|
397
|
+
const elements = (Array.isArray(doc.elements) ? doc.elements : []).filter(
|
|
398
|
+
(element: unknown) =>
|
|
399
|
+
isRecord(element) && typeof element.name === "string" && ownsElement(element.name, namespace),
|
|
400
|
+
);
|
|
401
|
+
return JSON.stringify({ ...doc, info: { ...doc.info, namespace }, elements });
|
|
402
|
+
}
|
|
403
|
+
|
|
331
404
|
export function extractFixtures(
|
|
332
405
|
zip: ZipAccessor,
|
|
333
406
|
manifest: readonly SyncManifestEntry[] = SYNC_MANIFEST,
|
|
@@ -339,10 +412,11 @@ export function extractFixtures(
|
|
|
339
412
|
throw new Error(`zip is missing entry ${source} for namespace ${item.namespace}`);
|
|
340
413
|
}
|
|
341
414
|
}
|
|
342
|
-
const
|
|
415
|
+
const merged =
|
|
343
416
|
item.mergeEntries === undefined
|
|
344
417
|
? zip.read(item.zipEntry)
|
|
345
418
|
: JSON.stringify(mergeApiDocs(sources.map((source) => JSON.parse(zip.read(source)))));
|
|
419
|
+
const contents = item.split ? splitNamespace(merged, item.namespace) : merged;
|
|
346
420
|
return { namespace: item.namespace, fixture: item.fixture, contents };
|
|
347
421
|
});
|
|
348
422
|
}
|
|
@@ -602,24 +676,26 @@ if (import.meta.main) {
|
|
|
602
676
|
const coreFixtures = extractFixtures(zip);
|
|
603
677
|
const extensionFixtures = await downloadExtensionFixtures();
|
|
604
678
|
const luaStdlibFixtures = extractFixtures(zip, LUA_STDLIB_MANIFEST);
|
|
679
|
+
const editorFixtures = extractFixtures(zip, EDITOR_MANIFEST);
|
|
680
|
+
const editorVmFixtures = extractFixtures(zip, EDITOR_VM_MANIFEST);
|
|
605
681
|
const results = [
|
|
606
682
|
...syncExtractedFixtures(coreFixtures, { check }),
|
|
607
683
|
...syncExtractedFixtures(extensionFixtures, { check }),
|
|
608
684
|
...syncExtractedFixtures(luaStdlibFixtures, { check }),
|
|
685
|
+
...syncExtractedFixtures(editorFixtures, { check }),
|
|
686
|
+
...syncExtractedFixtures(editorVmFixtures, { check }),
|
|
609
687
|
];
|
|
610
688
|
const syncedDocs = [...coreFixtures, ...extensionFixtures].map((f) => ({
|
|
611
689
|
namespace: f.namespace,
|
|
612
690
|
doc: JSON.parse(f.contents),
|
|
613
691
|
}));
|
|
614
692
|
const report = buildCoverageReport({
|
|
615
|
-
manifest:
|
|
693
|
+
manifest: COVERAGE_MANIFEST,
|
|
616
694
|
moduleManifest: MODULE_MANIFEST,
|
|
617
695
|
unmapped: UNMAPPED,
|
|
618
696
|
syncedDocs,
|
|
619
697
|
upstream: collectUpstreamNamespaces(zip),
|
|
620
|
-
upstreamMapped:
|
|
621
|
-
[...SYNC_MANIFEST, ...EXTENSION_MANIFEST, ...LUA_STDLIB_MANIFEST].map((e) => e.namespace),
|
|
622
|
-
),
|
|
698
|
+
upstreamMapped: UPSTREAM_MAPPED_NAMESPACES,
|
|
623
699
|
ignoredUpstream: IGNORED_UPSTREAM,
|
|
624
700
|
});
|
|
625
701
|
printReport(results, report, check);
|
package/src/core-types.ts
CHANGED
|
@@ -88,6 +88,7 @@ export interface Matrix4 {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
declare const HashBrand: unique symbol;
|
|
91
|
+
declare const HashSource: unique symbol;
|
|
91
92
|
/**
|
|
92
93
|
* An opaque, branded handle to a *hashed name*: hold it and pass it back to the
|
|
93
94
|
* engine API, but never inspect or construct it. Defold uses it in place of a
|
|
@@ -111,9 +112,22 @@ declare const HashBrand: unique symbol;
|
|
|
111
112
|
* `hash_to_hex(h)` renders it as a hexadecimal string for logging, and `pprint`
|
|
112
113
|
* shows it as `hash: [0x…]`. Two hashes are equal exactly when they name the
|
|
113
114
|
* same thing, so a `Hash` is safe to compare, store, and use as a table key.
|
|
115
|
+
*
|
|
116
|
+
* The `S` parameter records the string `hash()` was called with, so a tool can
|
|
117
|
+
* *read* an address back out of `const SPRITE = hash("#sprite")`. It is not a
|
|
118
|
+
* checked constraint: it defaults to `string`, so every bare `Hash` keeps
|
|
119
|
+
* meaning what it always did, and its phantom member is optional, so `Hash` and
|
|
120
|
+
* `Hash<"#sprite">` stay assignable to each other in both directions. Nothing
|
|
121
|
+
* that compiles without it stops compiling with it.
|
|
114
122
|
*/
|
|
115
|
-
export interface Hash {
|
|
123
|
+
export interface Hash<S extends string = string> {
|
|
116
124
|
readonly [HashBrand]: "Hash";
|
|
125
|
+
// Method syntax, and load-bearing: a method's parameters are compared
|
|
126
|
+
// bivariantly, which is what keeps `Hash` and `Hash<"#sprite">` assignable in
|
|
127
|
+
// *both* directions. Written as a plain optional property (`?: S`) the
|
|
128
|
+
// parameter would be covariant, and passing a bare `Hash` where a sourced one
|
|
129
|
+
// is expected would start failing — the one thing this type must never do.
|
|
130
|
+
[HashSource]?(source: S): void;
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
declare const OpaqueBrand: unique symbol;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** @noSelfInFile */
|
|
2
|
+
|
|
3
|
+
import type { Opaque } from "./core-types";
|
|
4
|
+
import type { EditorCommand, EditorCommandQuery } from "./editor";
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
namespace editor {
|
|
8
|
+
/**
|
|
9
|
+
* Create an editor command
|
|
10
|
+
*
|
|
11
|
+
* @param opts - A table with the following keys:`label string, message`required, user-visible command name, either a string or a localization message`locations string[]`required, a non-empty list of locations where the command is displayed in the editor, values are either `"Edit"`, `"View"`, `"Project"`, `"Debug"` (the editor menubar), `"Assets"` (the assets pane), or `"Outline"` (the outline pane)`query table`optional, a query that both controls the command availability and provides additional information to the command handler functions; a table with the following keys:`selection table`current selection, a table with the following keys:`type string`either `"resource"` (selected resource) or `"outline"` (selected outline node)`cardinality string`either `"one"` (will use first selected item) or `"many"` (will use all selected items)`argument table`the command argument`id string`optional, keyword identifier that may be used for assigning a shortcut to a command; should be a dot-separated identifier string, e.g. `"my-extension.do-stuff"``active function`optional function that additionally checks if a command is active in the current context; will receive opts table with values populated by the query; should be fast to execute since the editor might invoke it in response to UI interactions (on key typed, mouse clicked)`run function`optional function that is invoked when the user decides to execute the command; will receive opts table with values populated by the query
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // Print Git history for a file:
|
|
15
|
+
* // (`locations` is added here: upstream's own example omits it, but its
|
|
16
|
+
* // prose declares the key required.)
|
|
17
|
+
* editor.command({
|
|
18
|
+
* label: "Git History",
|
|
19
|
+
* locations: ["Assets"],
|
|
20
|
+
* query: { selection: { type: "resource", cardinality: "one" } },
|
|
21
|
+
* run: (opts) => {
|
|
22
|
+
* editor.execute("git", "log", "--follow", `.${editor.get(opts.selection, "path")}`, {
|
|
23
|
+
* reload_resources: false,
|
|
24
|
+
* });
|
|
25
|
+
* },
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
function command<const Q extends EditorCommandQuery = Record<never, never>>(
|
|
30
|
+
opts: EditorCommand<Q>,
|
|
31
|
+
): Opaque<"command">;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/** @noSelfInFile */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
HttpRouteHandler,
|
|
5
|
+
ZipEntries,
|
|
6
|
+
ZipPackOptions,
|
|
7
|
+
ZipUnpackOptions,
|
|
8
|
+
} from "./editor-vm-types";
|
|
9
|
+
|
|
10
|
+
// The editor VM surfaces the emit leaves behind, hand-authored and pinned to
|
|
11
|
+
// their vendored fixtures by `test/editor-vm-globals-parity.test.ts`. Three
|
|
12
|
+
// shapes land here: `pprint` is a bare global function with no namespace to hang
|
|
13
|
+
// off; the two-segment VARIABLEs (`zip.METHOD.*`, `http.server.*`) are reachable
|
|
14
|
+
// by the emitter's nested pass but deliberately withheld, because a VARIABLE
|
|
15
|
+
// carries no type and would emit as `unknown` — useless to `ZipPackOptions`,
|
|
16
|
+
// which types `method` as a string — while its brief is an unreliable literal
|
|
17
|
+
// (upstream's own `zip.ON_CONFLICT.OVERWRITE` reads `"skip"`); and the functions
|
|
18
|
+
// whose *vendored signature* cannot be rendered soundly — an optional parameter
|
|
19
|
+
// sitting before a required one, or an empty `returnvalues` on a function
|
|
20
|
+
// upstream's own prose says returns a value — are written out here as overload
|
|
21
|
+
// sets. All three are withheld from the emit by `EDITOR_VM_SKIP_FUNCTIONS`. The
|
|
22
|
+
// contract is unchanged and still derived: this file declares exactly what the
|
|
23
|
+
// emit leaves behind. The namespaces below merge with the emitted
|
|
24
|
+
// `generated/editor-vm/` bodies rather than replacing them. Upstream records no
|
|
25
|
+
// type for a VARIABLE, so those annotations are read from each member's prose.
|
|
26
|
+
|
|
27
|
+
declare global {
|
|
28
|
+
/**
|
|
29
|
+
* Pretty-print a Lua value
|
|
30
|
+
*/
|
|
31
|
+
function pprint(value: unknown): void;
|
|
32
|
+
|
|
33
|
+
namespace http {
|
|
34
|
+
namespace server {
|
|
35
|
+
/**
|
|
36
|
+
* Editor's HTTP server local url
|
|
37
|
+
*/
|
|
38
|
+
const local_url: string;
|
|
39
|
+
/**
|
|
40
|
+
* Editor's HTTP server port
|
|
41
|
+
*/
|
|
42
|
+
const port: number;
|
|
43
|
+
/**
|
|
44
|
+
* Editor's HTTP server url
|
|
45
|
+
*/
|
|
46
|
+
const url: string;
|
|
47
|
+
/**
|
|
48
|
+
* Create route definition for the editor's HTTP server
|
|
49
|
+
*
|
|
50
|
+
* `method`, `as` and `openapi` are optional but sit before the required
|
|
51
|
+
* `handler`, so each documented call shape is its own overload. An optional
|
|
52
|
+
* is supplied only when every earlier optional is: `method` and `as` are
|
|
53
|
+
* both strings, so a form that skipped `method` alone would be
|
|
54
|
+
* indistinguishable from one that supplied it.
|
|
55
|
+
*
|
|
56
|
+
* @param path - HTTP URI path, starts with `/`; may include path patterns (`{name}` for a single segment and `{*name}` for the rest of the request path) that will be extracted from the path and provided to the handler as a part of the request
|
|
57
|
+
* @param method - HTTP request method, default `"GET"`
|
|
58
|
+
* @param as - Request body converter, either `"string"` or `"json"`; the body will be discarded if not specified
|
|
59
|
+
* @param openapi - Optional OpenAPI Operation Object for this route method, exposed from `/openapi.json`
|
|
60
|
+
* @param handler - Request handler function, receives the request table and returns either a single response value, or 0 or more arguments to `http.server.response()`
|
|
61
|
+
* @returns HTTP server route
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* // Receive JSON and respond with JSON:
|
|
65
|
+
* http.server.route("/json", "POST", "json", (request) => {
|
|
66
|
+
* pprint(request);
|
|
67
|
+
* return 200;
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* // Extract parts of the path:
|
|
71
|
+
* http.server.route("/users/{user}/orders", (request) => {
|
|
72
|
+
* print((request as { user: string }).user);
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
function route(path: string, handler: HttpRouteHandler): unknown;
|
|
77
|
+
function route(path: string, method: string, handler: HttpRouteHandler): unknown;
|
|
78
|
+
function route(path: string, method: string, as: string, handler: HttpRouteHandler): unknown;
|
|
79
|
+
function route(
|
|
80
|
+
path: string,
|
|
81
|
+
method: string,
|
|
82
|
+
as: string,
|
|
83
|
+
openapi: Record<string | number, unknown>,
|
|
84
|
+
handler: HttpRouteHandler,
|
|
85
|
+
): unknown;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
namespace json {
|
|
90
|
+
/**
|
|
91
|
+
* Decode JSON string to Lua value
|
|
92
|
+
*
|
|
93
|
+
* Upstream records no return value while its own brief names one, so the
|
|
94
|
+
* return is written here. `unknown` is the honest answer: `options.all`
|
|
95
|
+
* makes the result an array, and a JSON document may be a scalar.
|
|
96
|
+
*
|
|
97
|
+
* @param json - json data
|
|
98
|
+
* @param options - A table with the following keys:`all boolean`if `true`, decodes all json values in a string and returns an array
|
|
99
|
+
* @returns the decoded value
|
|
100
|
+
*/
|
|
101
|
+
function decode(json: string, options?: Record<string | number, unknown>): unknown;
|
|
102
|
+
/**
|
|
103
|
+
* Encode Lua value to JSON string
|
|
104
|
+
*
|
|
105
|
+
* @param value - any Lua value that may be represented as JSON
|
|
106
|
+
* @returns the encoded document
|
|
107
|
+
*/
|
|
108
|
+
function encode(value: unknown): string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
namespace zip {
|
|
112
|
+
namespace METHOD {
|
|
113
|
+
/**
|
|
114
|
+
* `"deflated"` compression method
|
|
115
|
+
*/
|
|
116
|
+
const DEFLATED: string;
|
|
117
|
+
/**
|
|
118
|
+
* `"stored"` compression method, i.e. no compression
|
|
119
|
+
*/
|
|
120
|
+
const STORED: string;
|
|
121
|
+
}
|
|
122
|
+
namespace ON_CONFLICT {
|
|
123
|
+
/**
|
|
124
|
+
* `"error"`, any conflict aborts extraction
|
|
125
|
+
*/
|
|
126
|
+
const ERROR: string;
|
|
127
|
+
/**
|
|
128
|
+
* `"skip"`, existing file is preserved
|
|
129
|
+
*/
|
|
130
|
+
const SKIP: string;
|
|
131
|
+
/**
|
|
132
|
+
* `"skip"`, existing file is overwritten
|
|
133
|
+
*/
|
|
134
|
+
const OVERWRITE: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create a ZIP archive
|
|
138
|
+
*
|
|
139
|
+
* `opts` is optional but sits before the required `entries`, so the two
|
|
140
|
+
* documented call shapes are separate overloads.
|
|
141
|
+
*
|
|
142
|
+
* @param output_path - output zip file path, resolved against project root if relative
|
|
143
|
+
* @param opts - compression options
|
|
144
|
+
* @param entries - entries to compress, either a relative path or a list of entries
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* // Archive a file and a folder:
|
|
148
|
+
* zip.pack("build.zip", ["build", "game.project"]);
|
|
149
|
+
*
|
|
150
|
+
* // Change the location of the files within the archive:
|
|
151
|
+
* zip.pack("build.zip", [
|
|
152
|
+
* ["build/wasm-web", "."],
|
|
153
|
+
* ["configs/prod.json", "config.json"],
|
|
154
|
+
* ]);
|
|
155
|
+
*
|
|
156
|
+
* // Create archive without compression:
|
|
157
|
+
* zip.pack("build.zip", { method: zip.METHOD.STORED }, ["build", "resources"]);
|
|
158
|
+
*
|
|
159
|
+
* // Don't compress one of the folders:
|
|
160
|
+
* zip.pack("build.zip", [{ 1: "assets", method: zip.METHOD.STORED }, "build/wasm-web"]);
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
function pack(output_path: string, entries: ZipEntries): void;
|
|
164
|
+
function pack(output_path: string, opts: ZipPackOptions, entries: ZipEntries): void;
|
|
165
|
+
/**
|
|
166
|
+
* Extract a ZIP archive
|
|
167
|
+
*
|
|
168
|
+
* `target_path`, `opts` and `paths` are all optional and share the second
|
|
169
|
+
* slot, so the shape-discriminated forms come first: an all-optional
|
|
170
|
+
* `ZipUnpackOptions` cannot absorb a paths array, and vice versa.
|
|
171
|
+
*
|
|
172
|
+
* @param archive_path - zip file path, resolved against project root if relative
|
|
173
|
+
* @param target_path - target path for extraction, defaults to parent of `archive_path` if omitted
|
|
174
|
+
* @param opts - extraction options
|
|
175
|
+
* @param paths - entries to extract, relative string paths
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* // Extract everything next to the archive:
|
|
179
|
+
* zip.unpack("build/dev/resources.zip");
|
|
180
|
+
*
|
|
181
|
+
* // Extract to a different directory:
|
|
182
|
+
* zip.unpack("build/dev/resources.zip", "build/dev/tmp");
|
|
183
|
+
*
|
|
184
|
+
* // Extract while overwriting existing files on conflict:
|
|
185
|
+
* zip.unpack("build/dev/resources.zip", { on_conflict: zip.ON_CONFLICT.OVERWRITE });
|
|
186
|
+
*
|
|
187
|
+
* // Extract a single file:
|
|
188
|
+
* zip.unpack("build/dev/resources.zip", ["config.json"]);
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
function unpack(archive_path: string, paths: readonly string[]): void;
|
|
192
|
+
function unpack(archive_path: string, opts: ZipUnpackOptions, paths?: readonly string[]): void;
|
|
193
|
+
function unpack(
|
|
194
|
+
archive_path: string,
|
|
195
|
+
target_path?: string,
|
|
196
|
+
opts?: ZipUnpackOptions,
|
|
197
|
+
paths?: readonly string[],
|
|
198
|
+
): void;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** @noSelfInFile */
|
|
2
|
+
|
|
3
|
+
// The named types the hand-authored editor VM overloads take. They live outside
|
|
4
|
+
// `src/editor-vm-globals.d.ts` on purpose: the parity test compares that file's
|
|
5
|
+
// declared set to the set the emit leaves behind, exactly, and a type alias
|
|
6
|
+
// declared there would count as an extra declaration with no upstream member to
|
|
7
|
+
// answer for it.
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* One entry in a `zip.pack` archive: a source path, a `[source, target]` pair
|
|
11
|
+
* that relocates it inside the archive, or a table carrying per-entry
|
|
12
|
+
* compression options alongside those positions.
|
|
13
|
+
*/
|
|
14
|
+
export type ZipEntry = string | readonly string[] | Record<string | number, unknown>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* What `zip.pack` compresses: a single relative path, or a list of entries.
|
|
18
|
+
*/
|
|
19
|
+
export type ZipEntries = string | readonly ZipEntry[];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Archive-wide compression options for `zip.pack`.
|
|
23
|
+
*/
|
|
24
|
+
export interface ZipPackOptions {
|
|
25
|
+
/** Compression method, either `zip.METHOD.DEFLATED` (default) or `zip.METHOD.STORED`. */
|
|
26
|
+
readonly method?: string;
|
|
27
|
+
/** Compression level, 0 to 9; only useful with `zip.METHOD.DEFLATED`, defaults to 6. */
|
|
28
|
+
readonly level?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Extraction options for `zip.unpack`. Every key is optional, which is what lets
|
|
33
|
+
* TypeScript tell this table apart from the paths list that shares its slot.
|
|
34
|
+
*/
|
|
35
|
+
export interface ZipUnpackOptions {
|
|
36
|
+
/** Conflict resolution strategy, defaults to `zip.ON_CONFLICT.ERROR`. */
|
|
37
|
+
readonly on_conflict?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A `http.server.route` handler. Left at the looseness the emit gave it: typing
|
|
42
|
+
* the request table is a separate slice.
|
|
43
|
+
*/
|
|
44
|
+
export type HttpRouteHandler = (...args: unknown[]) => unknown;
|
package/src/editor.ts
CHANGED
|
@@ -3,36 +3,137 @@
|
|
|
3
3
|
// makes them a fourth, disjoint script kind — lowered to a chunk-level
|
|
4
4
|
// `return <hooks table>` rather than the runtime kinds' flat top-level globals.
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
6
|
+
// The typed `editor.*` global (`get`/`transact`/`command`) and the walled
|
|
7
|
+
// `@defold-typescript/types/editor-script` entrypoint ship alongside this file,
|
|
8
|
+
// as do the editor VM's own `http`/`json`/`zip`/`zlib`/`tilemap.tiles`
|
|
9
|
+
// libraries and the `editor.ui.*`, `editor.prefs.*`, and `localization.*`
|
|
10
|
+
// surfaces.
|
|
11
|
+
|
|
12
|
+
import type { Opaque } from "./core-types";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* What the editor addresses a node by: either a resource path (e.g.
|
|
16
|
+
* `"/main/game.script"`) or an internal node id the editor hands to the script.
|
|
17
|
+
* This is the argument `editor.get` and the `editor.tx.*` builders take.
|
|
18
|
+
*/
|
|
19
|
+
export type EditorNode = string | Opaque<"userdata">;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Declares a command's context: what the editor resolves before calling the
|
|
23
|
+
* command's hooks, and therefore which members its opts bag carries. Both keys
|
|
24
|
+
* are optional; a command that declares neither receives an empty bag.
|
|
25
|
+
*/
|
|
26
|
+
export interface EditorCommandQuery {
|
|
27
|
+
/**
|
|
28
|
+
* The current selection. `type` picks the selection source, `cardinality`
|
|
29
|
+
* picks between the first selected item and all of them.
|
|
30
|
+
*/
|
|
31
|
+
selection?: { type: "resource" | "outline"; cardinality: "one" | "many" };
|
|
32
|
+
/** Requests the command argument. Declared as an empty table. */
|
|
33
|
+
argument?: Record<string, never>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The opts bag the editor populates for a command whose query is `Q` — exactly
|
|
38
|
+
* the members `Q` declared, and no others.
|
|
39
|
+
*
|
|
40
|
+
* Each half branches on *key presence* (`"selection" extends keyof Q`) rather
|
|
41
|
+
* than on the property type: an optional property is still in `keyof`, so
|
|
42
|
+
* presence is what separates a declared query from the erased one. The
|
|
43
|
+
* cardinality test is wrapped in a one-tuple so a union (or `never`) does not
|
|
44
|
+
* distribute across the conditional.
|
|
45
|
+
*/
|
|
46
|
+
export type EditorCommandOpts<Q extends EditorCommandQuery> = ("selection" extends keyof Q
|
|
47
|
+
? {
|
|
48
|
+
selection: [NonNullable<Q["selection"]>["cardinality"]] extends ["many"]
|
|
49
|
+
? EditorNode[]
|
|
50
|
+
: EditorNode;
|
|
51
|
+
}
|
|
52
|
+
: Record<never, never>) &
|
|
53
|
+
("argument" extends keyof Q ? { argument: Record<string, unknown> } : Record<never, never>);
|
|
10
54
|
|
|
11
55
|
/**
|
|
12
56
|
* A single command an editor script contributes: a label, the editor UI
|
|
13
57
|
* locations it appears in (e.g. `"Edit"`, `"Assets"`, `"Outline"`, `"View"`),
|
|
14
|
-
* and optional `active`/`run` hooks the editor calls with
|
|
58
|
+
* and optional `active`/`run` hooks the editor calls with the context bag its
|
|
59
|
+
* own `query` declared.
|
|
60
|
+
*
|
|
61
|
+
* The editor calls `active`/`run` as plain functions, so they must emit no
|
|
62
|
+
* leading self parameter. `@noSelf` has to sit here, on the enclosing
|
|
63
|
+
* interface: TSTL resolves a hook's self context from its contextual signature,
|
|
64
|
+
* and the property-signature branch consults only this interface — it returns
|
|
65
|
+
* before `noImplicitSelf` or a file-level `@noSelfInFile` is ever considered.
|
|
66
|
+
*
|
|
67
|
+
* @noSelf
|
|
15
68
|
*/
|
|
16
|
-
export interface EditorCommand {
|
|
69
|
+
export interface EditorCommand<Q extends EditorCommandQuery = EditorCommandQuery> {
|
|
17
70
|
/** Menu/label text shown for the command. */
|
|
18
71
|
label: string;
|
|
19
72
|
/** Editor UI locations the command is offered in. */
|
|
20
73
|
locations: string[];
|
|
21
74
|
/**
|
|
22
75
|
* Declares the command's context arguments; the editor passes the resolved
|
|
23
|
-
* values to `active`/`run
|
|
76
|
+
* values to `active`/`run` as exactly the matching opts members.
|
|
24
77
|
*/
|
|
25
|
-
query?:
|
|
78
|
+
query?: Q;
|
|
26
79
|
/**
|
|
27
80
|
* Called to decide whether the command is currently enabled. Omit to always
|
|
28
|
-
* enable.
|
|
29
|
-
*/
|
|
30
|
-
active?: (opts: Record<string, unknown>) => boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Called when the command is invoked. The opts bag is loosely typed until the
|
|
33
|
-
* `editor.*` slice lands.
|
|
81
|
+
* enable. Should be fast — the editor may call it on key/mouse events.
|
|
34
82
|
*/
|
|
35
|
-
|
|
83
|
+
active?: (opts: EditorCommandOpts<Q>) => boolean;
|
|
84
|
+
/** Called when the user invokes the command. */
|
|
85
|
+
run?: (opts: EditorCommandOpts<Q>) => void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A command as it sits in a `get_commands` list: the same shape with its query
|
|
90
|
+
* erased. The hooks take `never`, the one parameter type every concrete opts
|
|
91
|
+
* bag is assignable *from* under parameter contravariance — so a `"one"` and a
|
|
92
|
+
* `"many"` command coexist in a single array while each keeps its own precise
|
|
93
|
+
* typing at its `defineEditorCommand` call site.
|
|
94
|
+
*/
|
|
95
|
+
export interface EditorCommandEntry {
|
|
96
|
+
/** Menu/label text shown for the command. */
|
|
97
|
+
label: string;
|
|
98
|
+
/** Editor UI locations the command is offered in. */
|
|
99
|
+
locations: string[];
|
|
100
|
+
/** Declares the command's context arguments. */
|
|
101
|
+
query?: EditorCommandQuery;
|
|
102
|
+
/** Called to decide whether the command is currently enabled. */
|
|
103
|
+
active?: (opts: never) => boolean;
|
|
104
|
+
/** Called when the user invokes the command. */
|
|
105
|
+
run?: (opts: never) => void;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Type a single editor command so its `active`/`run` hooks receive exactly the
|
|
110
|
+
* opts members its own `query` declared. At runtime this is an identity
|
|
111
|
+
* function — it returns `command` unchanged; its only job is typing, and the
|
|
112
|
+
* transpiler emits the table literal it wraps.
|
|
113
|
+
*
|
|
114
|
+
* @param command - the command to type and return.
|
|
115
|
+
* @returns the same `command`, typed as a query-erased list entry.
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* export default defineEditorScript({
|
|
119
|
+
* get_commands: () => [
|
|
120
|
+
* defineEditorCommand({
|
|
121
|
+
* label: "Git History",
|
|
122
|
+
* locations: ["Assets"],
|
|
123
|
+
* query: { selection: { type: "resource", cardinality: "one" } },
|
|
124
|
+
* run: (opts) => print(editor.get(opts.selection, "path")),
|
|
125
|
+
* }),
|
|
126
|
+
* ],
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
// The `Record<never, never>` default (not `Record<string, never>`, whose `keyof`
|
|
131
|
+
// is `string` and would match every presence test) gives a command that declares
|
|
132
|
+
// no query an empty opts bag.
|
|
133
|
+
export function defineEditorCommand<const Q extends EditorCommandQuery = Record<never, never>>(
|
|
134
|
+
command: EditorCommand<Q>,
|
|
135
|
+
): EditorCommandEntry {
|
|
136
|
+
return command as EditorCommandEntry;
|
|
36
137
|
}
|
|
37
138
|
|
|
38
139
|
/**
|
|
@@ -41,7 +142,7 @@ export interface EditorCommand {
|
|
|
41
142
|
*/
|
|
42
143
|
export interface EditorScriptModule {
|
|
43
144
|
/** Returns the commands this script contributes to the editor. */
|
|
44
|
-
get_commands?: () =>
|
|
145
|
+
get_commands?: () => EditorCommandEntry[];
|
|
45
146
|
/** Returns language-server descriptors this script contributes. */
|
|
46
147
|
get_language_servers?: () => unknown[];
|
|
47
148
|
}
|