Skip to content

Add external-id attribute#672

Open
lukewagner wants to merge 3 commits into
mainfrom
external-id
Open

Add external-id attribute#672
lukewagner wants to merge 3 commits into
mainfrom
external-id

Conversation

@lukewagner

@lukewagner lukewagner commented Jun 29, 2026

Copy link
Copy Markdown
Member

This PR extends #613, which added an (implements <interfacename>) attribute to imports and exports, with an (external-id <string>) attribute. The external-id attribute is useful when a component import or export name wants to match some external identifier, but the kebab-case syntax is too restrictive. One example is ESM-integration, where Module Specifiers can (now) be arbitrary Unicode strings (including URLs). Another is when importing stateful stores, e.g.:

world w {
  import redis: wasi:keyvalue/store;
  import memcached: wasi:keyvalue/store;
}

(as enabled by #613) and the platform-defined identifier of these stores isn't a valid <plainname>, so you'd either have to add a whole separate name-mapping scheme (e.g. mapping redis and memcached to their host IDs) or mangle the host IDs into the <plainname> (say, via URL-encoding; ew). Instead, with this PR, you can use the @external-id attribute in WIT:

world w {
  @external-id("$whatever:host_id")
  import redis: wasi:keyvalue/store;

  @external-id("$something:else")
  import memcached: wasi:keyvalue/store;
}

and the WAT (external-id <string>) is plumbed into the host, alongside (implements <interfacename>), so that the host can reflect on it and act accordingly.

As mentioned above, @external-id can subsume the use cases for url=<...> names for ESM-integration. Moreover, the reasoning in #613 for moving implements out of the name and into a separate attribute I think similarly suggests that the <urlname>, <depname> and <hashname> cases of <importname> aren't really the right place to solve their associated use cases. IIUC, these 3 name cases aren't currently being produced since there's no WIT syntax to emit them. Thus, this PR tentatively proposes removing these 3 name cases. The use cases are still important, but I think when we're ready to prioritize adding them to WIT/tooling, we should consider adding them as attributes instead. In the meantime, it lets us merge importname and exportname into externname, which simplifies nicely things.

The PR also tidies up some sloppiness in the text-format grammar around string literals (e.g., "foo-bar") vs. the contents inside the quotes (e.g. foo-bar), using <Xstr> for the former and <X> for the latter.

@lukewagner lukewagner force-pushed the external-id branch 2 times, most recently from 9e2219f to eeb2ed0 Compare June 30, 2026 15:47

@ricochet ricochet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. I'd use it right away.

You may want to update Linking.md as well:

mutually known to later stages in the deployment pipeline (specifically with
the [depname] case of importname in the text and binary format).

To something like:

mutually known to later stages in the deployment pipeline. (How an import
name is resolved to a particular registry artifact is left to the host or
build tooling; the optional [external-id] attribute can carry a
tooling-specific identifier such as a registry path or URL.)

@lukewagner

Copy link
Copy Markdown
Member Author

@ricochet Thanks, good catch! Fixed

Comment thread design/mvp/WIT.md Outdated
Comment on lines +1111 to +1117
### Strings

WIT string literals are double-quoted and use the same text format as the Core
WebAssembly text format's [`string`] literal. For example `"a"`, `"☃︎"`, `"\7f"`
and `"\u{7fff}"` are all `string` literals.

[`string`]: https://webassembly.github.io/spec/core/text/values.html#text-string

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarification on this: core wasm string literals are for encoding byte arrays (e.g. data segments), so could/should this additionally indicate that a utf-8 check is expected?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, you're right; I should switch these strings back to name which, as you said, is the utf-8 decoding of string.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcrichton

Copy link
Copy Markdown
Collaborator

I've got an initial implementation of this over at bytecodealliance/wasm-tools#2555 if you wouldn't mind skimming over the test I've added

@lukewagner

Copy link
Copy Markdown
Member Author

Awesome! Test looks good.

Comment thread design/mvp/Binary.md
* 🏷️ Validation requires that `implements`-annotated imports or exports have a
`<plainname>` name.
`instance`-typed and have a `plainname` name.
* 🏷️/🔗 Validation requires that a `vec(<attribute>)` contains each kind of

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 🏷️/🔗 Validation requires that a `vec(<attribute>)` contains each kind of
* 🏷️ Validation requires that `external-id`-annotated imports or exports are
`instance`-typed.
* 🏷️/🔗 Validation requires that a `vec(<attribute>)` contains each kind of

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't external-id be like implements and be only on instances?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch; I totally missed this discrepancy between the PR and @alexcrichton's above-linked test case. So while implements is inherently tied to a WIT interface (via interfacename) which is inherently tied to an instance type, external-id has no such tie. I think there are also real examples where you'd naturally want to put @external-id on other types of imports/exports (e.g., nullary functions returning capabilities) and indeed WIT.md already includes some (toy) examples that do this too. So perhaps we could keep the spec as-is and change the above test? WDYT @alexcrichton?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me yeah, and sorry forgot to flag this here as well. This restriction came about when I was working on the WIT side of things and I was wondering where to store external-id in the AST of a WIT package.

Would it be ok to limit this to just functions/interfaces for now? Or should types be included? Or perhaps components/etc as well? Or, I suppose another way to ask, should there be any validation of external_id when it's encountered?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since @external-ids are really just about closing the kebab-case-name-expressivity gap, I think the use cases for it on instances and funcs are about as strong as for having it on components, core modules and types. E.g., with ESM-integration, it seems valuable for a component to be able to give a core module- or component-typed import a URL external-id so that these dependencies can be fetched by the ESM machinery without needing an import-map. That leaves type; a concrete use case there might be importing a resource type that corresponds to some arbitrary JS identifier? So my inclination is that there would be no validation criteria concerning the type (only that there is at most one external-id in the attribute list). But let me know if that's too big of a lift for now.

Comment thread design/mvp/Explainer.md
Comment on lines +2600 to +2601
🏷️ The `external-id` attribute addresses the naming-expressivity gap left
between the restricted syntax of `externname` (shown next) and whatever

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
🏷️ The `external-id` attribute addresses the naming-expressivity gap left
between the restricted syntax of `externname` (shown next) and whatever
🏷️ Like `implements`, the `external-id` attribute may only be applied to
`instance`-typed imports and exports. It addresses the naming-expressivity gap
left between the restricted syntax of `externname` (shown next) and whatever

@ricochet ricochet requested review from alexcrichton and ricochet July 2, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants