{"id":22927351,"url":"https://github.com/cztomsik/napigen","last_synced_at":"2025-07-13T07:06:20.564Z","repository":{"id":59483232,"uuid":"536693636","full_name":"cztomsik/napigen","owner":"cztomsik","description":"Automatic N-API (server-side javascript) bindings for your Zig project.","archived":false,"fork":false,"pushed_at":"2025-04-02T13:13:23.000Z","size":84,"stargazers_count":58,"open_issues_count":3,"forks_count":7,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-13T01:45:34.845Z","etag":null,"topics":["javascript","napi","nodejs","zig","zig-package"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cztomsik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-09-14T17:39:50.000Z","updated_at":"2025-04-10T03:48:00.000Z","dependencies_parsed_at":"2024-03-14T12:31:30.967Z","dependency_job_id":"4812b2bf-8f7f-4236-8d7b-8f6ce002c771","html_url":"https://github.com/cztomsik/napigen","commit_stats":null,"previous_names":["cztomsik/napigen"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cztomsik%2Fnapigen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cztomsik%2Fnapigen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cztomsik%2Fnapigen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cztomsik%2Fnapigen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cztomsik","download_url":"https://codeload.github.com/cztomsik/napigen/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253856613,"owners_count":21974575,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","napi","nodejs","zig","zig-package"],"created_at":"2024-12-14T09:14:18.876Z","updated_at":"2025-05-13T01:45:35.132Z","avatar_url":"https://github.com/cztomsik.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# zig-napigen\n\nComptime N-API bindings for Zig.\n\n\u003e You need to use latest Zig 0.14.0 to use this library.\n\u003e\n\u003e See [ggml-js](https://github.com/cztomsik/ggml-js) for a complete, real-world\n\u003e example.\n\n## Features\n\n- Primitives, tuples, structs (value types), optionals\n- Strings (valid for the function scope)\n- Struct pointers (see below)\n- Functions (no classes, see below)\n- all the `napi_xxx` functions and types are re-exported as `napigen.napi_xxx`,\\\n  so you can do pretty much anything if you don't mind going lower-level.\n\n## Limited scope\n\nThe library provides a simple and thin API, supporting only basic types. This\ndesign choice is intentional, as it is often difficult to determine the ideal\nmapping for more complex types. The library allows users to hook into the\nmapping process or use the N-API directly for finer control.\n\nSpecifically, there is no support for classes.\n\n## Structs/tuples (value types)\n\nWhen returning a struct/tuple by value, it is mapped to an anonymous JavaScript\nobject/array with all properties/elements mapped recursively. Similarly, when\naccepting a struct/tuple by value, it is mapped back from JavaScript to the\nrespective native type.\n\nIn both cases, a copy is created, so changes to the JS object are not reflected\nin the native part and vice versa.\n\n## Struct pointers (\\*T)\n\nWhen returning a pointer to a struct, an empty JavaScript object will be created\nwith the pointer wrapped inside. If this JavaScript object is passed to a\nfunction that accepts a pointer, the pointer is unwrapped back.\n\nThe same JavaScript object is obtained for the same pointer, unless it has\nalready been collected. This is useful for attaching state to the JavaScript\ncounterpart and accessing that data later.\n\nChanges to JavaScript objects are not reflected in the native part, but\ngetters/setters can be provided in JavaScript and native functions can be called\nas necessary.\n\n## Functions\n\nJavaScript functions can be created with ctx.createFunction(zig_fn) and then\nexported like any other value. Only comptime-known functions are supported. If\nan error is returned from a function call, an exception is thrown in JavaScript.\n\n```zig\nfn add(a: i32, b: i32) i32 {\n    return a + b;\n}\n\n// Somewhere where the JsContext is available\nconst js_fun: napigen.napi_value = try js.createFunction(add);\n\n// Make the function accessible to JavaScript\ntry js.setNamedProperty(exports, \"add\", js_fun);\n```\n\nNote that **the number of arguments must match exactly**. So if you need to\nsupport optional arguments, you will have to provide a wrapper function in JS,\nwhich calls the native function with the correct arguments.\n\n## Callbacks, \\*JsContext, napi_value\n\nFunctions can also accept the current `*JsContext`, which is useful for calling\nthe N-API directly or performing callbacks. To get a raw JavaScript value,\nsimply use `napi_value` as an argument type.\n\n```zig\nfn callMeBack(js: *napigen.JsContext, recv: napigen.napi_value, fun: napigen.napi_value) !void {\n    try js.callFunction(recv, fun, .{ \"Hello from Zig\" });\n}\n```\n\nAnd then\n\n```javascript\nnative.callMeBack(console, console.log)\n```\n\nIf you need to store the callback for a longer period of time, you should create\na ref. For now, you have to do that directly, using `napi_create_reference()`.\n\n## defineModule(init_fn), exports\n\nN-API modules need to export a function which will also init \u0026 return the\n`exports` object. You could export `napi_register_module_v1` and call\n`JsContext.init()` yourself but there's also a shorthand using `comptime` block\nwhich will allow you to use `try` anywhere inside:\n\n```zig\ncomptime { napigen.defineModule(initModule) }\n\nfn initModule(js: *napigen.JsContext, exports: napigen.napi_value) anyerror!napigen.napi_value {\n    try js.setNamedProperty(exports, ...);\n    ...\n\n    return exports;\n}\n```\n\n## Hooks\n\nWhenever a value is passed from Zig to JS or vice versa, the library will call a\nhook function, if one is defined. This allows you to customize the mapping\nprocess.\n\nHooks have to be defined in the root module, and they need to be named\n`napigenRead` and `napigenWrite` respectively. They must have the following\nsignature:\n\n```zig\nfn napigenRead(js: *napigen.JsContext, comptime T: type, value: napigen.napi_value) !T {\n    return switch (T) {\n        // we can easily customize the mapping for specific types\n        // for example, we can allow passing regular JS strings anywhere where we expect an InternedString\n        InternedString =\u003e InternedString.from(try js.read([]const u8)),\n\n        // otherwise, just use the default mapping, note that this time\n        // we call js.defaultRead() explicitly, to avoid infinite recursion\n        else =\u003e js.defaultRead(T, value),\n    }\n}\n\npub fn napigenWrite(js: *napigen.JsContext, value: anytype) !napigen.napi_value {\n    return switch (@TypeOf(value) {\n        // convert InternedString to back to a JS string (hypothetically)\n        InternedString =\u003e try js.write(value.ptr),\n\n        // same thing here\n        else =\u003e js.defaultWrite(value),\n    }\n}\n```\n\n---\n\n## Complete example\n\nThe repository includes a complete example in the `example` directory. Here's a quick walkthrough:\n\n**1. Create a new library**\n\n```bash\nmkdir example\ncd example\nzig init-lib\n```\n\n**2. Add napigen as zig module.**\n\n```\nzig fetch --save git+https://github.com/cztomsik/napigen#main\n```\n\n**3. Update build.zig**\n\nThen, change your `build.zig` to something like this:\n\n```zig\nconst std = @import(\"std\");\nconst napigen = @import(\"napigen\");\n\npub fn build(b: *std.Build) void {\n    const target = b.standardTargetOptions(.{});\n    const optimize = b.standardOptimizeOption(.{});\n\n    const lib = b.addSharedLibrary(.{\n        .name = \"example\",\n        .root_source_file = b.path(\"src/main.zig\"),\n        .target = target,\n        .optimize = optimize,\n    });\n\n    // Add napigen\n    napigen.setup(lib);\n\n    // Build the lib\n    b.installArtifact(lib);\n\n    // Copy the result to a *.node file so we can require() it\n    const copy_node_step = b.addInstallLibFile(lib.getEmittedBin(), \"example.node\");\n    b.getInstallStep().dependOn(\u0026copy_node_step.step);\n}\n```\n\n**4. Define \u0026 export something useful**\n\nNext, define some functions and the N-API module itself in `src/main.zig`\n\n```zig\nconst std = @import(\"std\");\nconst napigen = @import(\"napigen\");\n\nexport fn add(a: i32, b: i32) i32 {\n    return a + b;\n}\n\ncomptime {\n    napigen.defineModule(initModule);\n}\n\nfn initModule(js: *napigen.JsContext, exports: napigen.napi_value) anyerror!napigen.napi_value {\n    try js.setNamedProperty(exports, \"add\", try js.createFunction(add));\n\n    return exports;\n}\n```\n\n**5. Use it from JS side**\n\nFinally, use it from JavaScript as expected:\n\n```javascript\nimport { createRequire } from 'node:module'\nconst require = createRequire(import.meta.url)\nconst native = require('./zig-out/lib/example.node')\n\nconsole.log('1 + 2 =', native.add(1, 2))\n```\n\nTo build the library and run the script:\n\n```\n\u003e zig build \u0026\u0026 node example.js\n1 + 2 = 3\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcztomsik%2Fnapigen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcztomsik%2Fnapigen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcztomsik%2Fnapigen/lists"}