Developers · Examples
Code examples, annotated.
Small, complete pieces you can copy. Each follows the IDOP 1.0 specification; the counter is a real package you can download and open.
§ 01A complete document
The counter.
A button whose count is saved in the file. Download it, open it in IDOP Cloud, click, save — and the count is in the file the next time you open it.
counter.idop
├── mimetype application/vnd.idop+zip
├── idop.json the manifest
├── code/
│ ├── index.html entry point
│ └── app.js
└── storage/
└── count.json written by the reader on Saveconst button = document.querySelector('#add');
let count = 0;
try {
count = JSON.parse((await idop.storage.read('count.json')).text).count;
} catch (error) {
if (error.code !== 'IDOP-STORAGE-NOT-FOUND') throw error;
}
button.textContent = String(count);
button.addEventListener('click', async () => {
count += 1;
button.textContent = String(count);
await idop.storage.write('count.json', JSON.stringify({ count }));
});§ 02Storage
Transactions, and asking to save.
// All or nothing: either both writes happen, or neither does.
await idop.storage.transaction([
{ op: 'write', path: 'cards.json', data: JSON.stringify(cards) },
{ op: 'write', path: 'meta.json', data: JSON.stringify({ updated: Date.now() }) },
]);
// Ask the reader to show its own Save control. The decision stays with the user.
await idop.ui.requestSave();§ 03Errors
Branch on the code, never the message.
Messages are diagnostics and may be localised. Codes are stable and listed in §16 of the specification.
try {
const { text } = await idop.storage.read('settings.json');
settings = JSON.parse(text);
} catch (error) {
switch (error.code) {
case 'IDOP-STORAGE-NOT-FOUND':
settings = defaults; // first run
break;
case 'IDOP-STORAGE-UTF8':
showProblem('Saved settings are not text.');
break;
default:
throw error; // never parse error.message
}
}§ 04Network
Calling an API with a key the document never sees.
Declare the capability and the credential binding in the manifest; the reader asks the user, holds the key, and attaches it only for the declared origin.
"requiredCapabilities": [{
"id": "ai",
"type": "network",
"origins": ["https://api.example.com"],
"methods": ["POST"],
"credentialBinding": "api-key",
"purpose": "Generate answers"
}],
"credentialBindings": [{
"id": "api-key",
"required": true,
"description": "Key for api.example.com"
}]// The document never sees the key: the reader adds it,
// and only for an origin the manifest declared.
const response = await idop.network.request({
capability: 'ai',
url: 'https://api.example.com/v1/answers',
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ question }),
});
if (response.status === 200) render(JSON.parse(response.body));§ 05Viewers
Opening ordinary files with an IDOP document.
A reader may let a user open, say, a .docx with an IDOP document as its viewer (§11.4, informative).
// A document opened as the viewer for an ordinary file
// receives exactly that one file, and nothing else.
const input = await idop.host.getInputFile();
// { name, extension, size, bytes }
render(input.name, input.bytes);
// Hand an edited copy back to the reader. This writes nothing by itself.
await idop.host.putOutputFile(editedBytes);More in the documentation.
IDOP Cloud runs in the browser. Start from a template, open a file you were sent, or keep your own — no installation, and no account needed just to open a document.