@oxy.so/core 1.7.1 → 1.7.4

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.
@@ -391,10 +391,13 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
391
391
  /**
392
392
  * Whether this process can prove what it is (ADR 0026).
393
393
  *
394
- * The check and the module that performs it are loaded LAZILY and only on a
395
- * Node host. `@oxy.so/core`'s root barrel reaches React Native and Expo, and
396
- * `server/workloadIdentity` imports `node:crypto` — a static import here
397
- * would pull it into every mobile bundle for a path a phone can never take.
394
+ * `#workload-identity` (package.json `imports`) selects a client
395
+ * implementation for native and browser bundlers, and the signer only for
396
+ * Node hosts. The runtime Node guard avoids calling attestation on clients;
397
+ * the module boundary keeps node:crypto out of their bundle graphs (a guard
398
+ * alone cannot). It is a package import, not a self-reference to an export
399
+ * subpath: a self-reference only resolves where `@oxy.so/core` is reachable
400
+ * through `node_modules`, which bundlers inside this workspace are not.
398
401
  *
399
402
  * @internal
400
403
  */
@@ -0,0 +1,4 @@
1
+ import type { WorkloadServiceToken, WorkloadServiceTokenOptions } from './workloadIdentity';
2
+ /** Workload attestation belongs to a Node host, never a browser or device. */
3
+ export declare function canAttestWorkloadIdentity(): boolean;
4
+ export declare function requestWorkloadServiceToken(_options: WorkloadServiceTokenOptions): Promise<WorkloadServiceToken>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/core",
3
- "version": "1.7.1",
3
+ "version": "1.7.4",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -119,7 +119,7 @@
119
119
  "@noble/ciphers": "^1.3.0",
120
120
  "@noble/hashes": "^1.8.0",
121
121
  "@oxy.so/contracts": "^1.3.0",
122
- "@oxy.so/protocol": "^1.0.1",
122
+ "@oxy.so/protocol": "^1.1.0",
123
123
  "@scure/bip39": "^1.6.0",
124
124
  "buffer": "^6.0.3",
125
125
  "express-rate-limit": "^8.6.0",
@@ -647,17 +647,20 @@ export function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T)
647
647
  /**
648
648
  * Whether this process can prove what it is (ADR 0026).
649
649
  *
650
- * The check and the module that performs it are loaded LAZILY and only on a
651
- * Node host. `@oxy.so/core`'s root barrel reaches React Native and Expo, and
652
- * `server/workloadIdentity` imports `node:crypto` — a static import here
653
- * would pull it into every mobile bundle for a path a phone can never take.
650
+ * `#workload-identity` (package.json `imports`) selects a client
651
+ * implementation for native and browser bundlers, and the signer only for
652
+ * Node hosts. The runtime Node guard avoids calling attestation on clients;
653
+ * the module boundary keeps node:crypto out of their bundle graphs (a guard
654
+ * alone cannot). It is a package import, not a self-reference to an export
655
+ * subpath: a self-reference only resolves where `@oxy.so/core` is reachable
656
+ * through `node_modules`, which bundlers inside this workspace are not.
654
657
  *
655
658
  * @internal
656
659
  */
657
660
  async _canUseWorkloadIdentity(): Promise<boolean> {
658
661
  if (typeof process === 'undefined' || !process.versions?.node) return false;
659
662
  try {
660
- const { canAttestWorkloadIdentity } = await import('../server/workloadIdentity');
663
+ const { canAttestWorkloadIdentity } = await import('#workload-identity');
661
664
  return canAttestWorkloadIdentity();
662
665
  } catch {
663
666
  // A bundler that dropped the server subpath, or a runtime without it.
@@ -684,7 +687,7 @@ export function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(Base: T)
684
687
  if (entry?.token && entry.expiresAt > now + 60_000) return entry.token;
685
688
  if (entry?.pending) return entry.pending;
686
689
 
687
- const { requestWorkloadServiceToken } = await import('../server/workloadIdentity');
690
+ const { requestWorkloadServiceToken } = await import('#workload-identity');
688
691
  const seeded = entry ?? {
689
692
  token: '',
690
693
  expiresAt: 0,
@@ -0,0 +1,16 @@
1
+ import { canAttestWorkloadIdentity, requestWorkloadServiceToken } from '../workloadIdentity.client';
2
+
3
+ describe('client workload identity boundary', () => {
4
+ it('cannot attest even when Node process globals are present', () => {
5
+ expect(process.versions.node).toBeTruthy();
6
+ expect(canAttestWorkloadIdentity()).toBe(false);
7
+ });
8
+
9
+ it('rejects before contacting a server when explicitly requested', async () => {
10
+ const fetchSpy = jest.spyOn(globalThis, 'fetch');
11
+ await expect(requestWorkloadServiceToken({ baseUrl: 'https://example.test' }))
12
+ .rejects.toThrow('only available on a Node host');
13
+ expect(fetchSpy).not.toHaveBeenCalled();
14
+ fetchSpy.mockRestore();
15
+ });
16
+ });
@@ -0,0 +1,12 @@
1
+ import type { WorkloadServiceToken, WorkloadServiceTokenOptions } from './workloadIdentity';
2
+
3
+ /** Workload attestation belongs to a Node host, never a browser or device. */
4
+ export function canAttestWorkloadIdentity(): boolean {
5
+ return false;
6
+ }
7
+
8
+ export async function requestWorkloadServiceToken(
9
+ _options: WorkloadServiceTokenOptions,
10
+ ): Promise<WorkloadServiceToken> {
11
+ throw new Error('Workload identity is only available on a Node host');
12
+ }